Perform NE_InitProcess in the context of the new task.
[wine] / loader / ne / segment.c
1 /*
2  * NE segment loading
3  *
4  * Copyright 1993 Robert J. Amstadt
5  * Copyright 1995 Alexandre Julliard
6  */
7
8 #include <assert.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <sys/types.h>
12 #include <sys/stat.h>
13 #include <fcntl.h>
14 #include <unistd.h>
15 #include <ctype.h>
16 #include <string.h>
17 #include <errno.h>
18
19 #include "wine/winbase16.h"
20 #include "neexe.h"
21 #include "global.h"
22 #include "task.h"
23 #include "selectors.h"
24 #include "callback.h"
25 #include "file.h"
26 #include "module.h"
27 #include "stackframe.h"
28 #include "builtin16.h"
29 #include "debugtools.h"
30 #include "toolhelp.h"
31
32 DECLARE_DEBUG_CHANNEL(dll)
33 DECLARE_DEBUG_CHANNEL(fixup)
34 DECLARE_DEBUG_CHANNEL(module)
35 DECLARE_DEBUG_CHANNEL(segment)
36
37 #define SEL(x) ((x)|1)
38
39 static void NE_FixupSegmentPrologs(NE_MODULE *pModule, WORD segnum);
40
41 /***********************************************************************
42  *           NE_GetRelocAddrName
43  */
44 static const char *NE_GetRelocAddrName( BYTE addr_type, int additive )
45 {
46     switch(addr_type & 0x7f)
47     {
48     case NE_RADDR_LOWBYTE:   return additive ? "BYTE add" : "BYTE";
49     case NE_RADDR_OFFSET16:  return additive ? "OFFSET16 add" : "OFFSET16";
50     case NE_RADDR_POINTER32: return additive ? "POINTER32 add" : "POINTER32";
51     case NE_RADDR_SELECTOR:  return additive ? "SELECTOR add" : "SELECTOR";
52     case NE_RADDR_POINTER48: return additive ? "POINTER48 add" : "POINTER48";
53     case NE_RADDR_OFFSET32:  return additive ? "OFFSET32 add" : "OFFSET32";
54     }
55     return "???";
56 }
57
58
59 /***********************************************************************
60  *           NE_LoadSegment
61  */
62 BOOL NE_LoadSegment( NE_MODULE *pModule, WORD segnum )
63 {
64     SEGTABLEENTRY *pSegTable, *pSeg;
65     WORD *pModuleTable;
66     WORD count, i, offset, next_offset;
67     HMODULE16 module;
68     FARPROC16 address = 0;
69     HFILE hf;
70     DWORD res;
71     struct relocation_entry_s *rep, *reloc_entries;
72     BYTE *func_name;
73     int size;
74     char* mem;
75
76     char buffer[256];
77     int ordinal, additive;
78     unsigned short *sp;
79
80     pSegTable = NE_SEG_TABLE( pModule );
81     pSeg = pSegTable + segnum - 1;
82
83     if (pSeg->flags & NE_SEGFLAGS_LOADED)
84     {
85         /* self-loader ? -> already loaded it */
86         if (pModule->flags & NE_FFLAGS_SELFLOAD)
87             return TRUE;
88
89         /* leave, except for DGROUP, as this may be the second instance */
90         if (segnum != pModule->dgroup)
91             return TRUE;
92     }
93
94     if (!pSeg->filepos) return TRUE;  /* No file image, just return */
95         
96     pModuleTable = NE_MODULE_TABLE( pModule );
97
98     hf = NE_OpenFile( pModule );
99     TRACE_(module)("Loading segment %d, hSeg=%04x, flags=%04x\n",
100                     segnum, pSeg->hSeg, pSeg->flags );
101     SetFilePointer( hf, pSeg->filepos << pModule->alignment, NULL, SEEK_SET );
102     if (pSeg->size) size = pSeg->size;
103     else size = pSeg->minsize ? pSeg->minsize : 0x10000;
104     mem = GlobalLock16(pSeg->hSeg);
105     if (pModule->flags & NE_FFLAGS_SELFLOAD && segnum > 1)
106     {
107         /* Implement self-loading segments */
108         SELFLOADHEADER *selfloadheader;
109         DWORD oldstack;
110         HFILE hFile32;
111         HFILE16 hFile16;
112
113         selfloadheader = (SELFLOADHEADER *)
114                 PTR_SEG_OFF_TO_LIN(SEL(pSegTable->hSeg),0);
115         oldstack = NtCurrentTeb()->cur_stack;
116         NtCurrentTeb()->cur_stack = PTR_SEG_OFF_TO_SEGPTR(pModule->self_loading_sel,
117                                                           0xff00 - sizeof(STACK16FRAME));
118
119         TRACE_(dll)("CallLoadAppSegProc(hmodule=0x%04x,hf=0x%04x,segnum=%d\n",
120                 pModule->self,hf,segnum );
121         DuplicateHandle( GetCurrentProcess(), hf, GetCurrentProcess(), &hFile32,
122                          0, FALSE, DUPLICATE_SAME_ACCESS );
123         hFile16 = FILE_AllocDosHandle( hFile32 );
124         pSeg->hSeg = Callbacks->CallLoadAppSegProc( selfloadheader->LoadAppSeg,
125                                                     pModule->self, hFile16,
126                                                     segnum );
127         TRACE_(dll)("Ret CallLoadAppSegProc: hSeg = 0x%04x\n", pSeg->hSeg);
128         _lclose16( hFile16 );
129         NtCurrentTeb()->cur_stack = oldstack;
130     }
131     else if (!(pSeg->flags & NE_SEGFLAGS_ITERATED))
132         ReadFile(hf, mem, size, &res, NULL);
133     else {
134       /*
135          The following bit of code for "iterated segments" was written without
136          any documentation on the format of these segments. It seems to work,
137          but may be missing something. If you have any doc please either send
138          it to me or fix the code yourself. gfm@werple.mira.net.au
139       */
140       char* buff = HeapAlloc(GetProcessHeap(), 0, size);
141       char* curr = buff;
142
143       if(buff == NULL) {
144           WARN_(dll)("Memory exausted!");
145           return FALSE;
146       }
147
148       ReadFile(hf, buff, size, &res, NULL);
149       while(curr < buff + size) {
150         unsigned int rept = *((short*) curr)++;
151         unsigned int len = *((short*) curr)++;
152         for(; rept > 0; rept--) {
153           char* bytes = curr;
154           unsigned int byte;
155           for(byte = 0; byte < len; byte++)
156             *mem++ = *bytes++;
157         }
158         curr += len;
159       }
160       HeapFree(GetProcessHeap(), 0, buff);
161     }
162
163     pSeg->flags |= NE_SEGFLAGS_LOADED;
164
165     /* Perform exported function prolog fixups */
166     NE_FixupSegmentPrologs( pModule, segnum );
167
168     if (!(pSeg->flags & NE_SEGFLAGS_RELOC_DATA))
169         return TRUE;  /* No relocation data, we are done */
170
171     ReadFile(hf, &count, sizeof(count), &res, NULL);
172     if (!count) return TRUE;
173
174     TRACE_(fixup)("Fixups for %.*s, segment %d, hSeg %04x\n",
175                    *((BYTE *)pModule + pModule->name_table),
176                    (char *)pModule + pModule->name_table + 1,
177                    segnum, pSeg->hSeg );
178     TRACE_(segment)("Fixups for %.*s, segment %d, hSeg %04x\n",
179                    *((BYTE *)pModule + pModule->name_table),
180                    (char *)pModule + pModule->name_table + 1,
181                    segnum, pSeg->hSeg );
182
183     reloc_entries = (struct relocation_entry_s *)HeapAlloc(GetProcessHeap(), 0, count * sizeof(struct relocation_entry_s));
184     if(reloc_entries == NULL) {
185         WARN_(fixup)("Not enough memory for relocation entries!");
186         return FALSE;
187     }
188     if (!ReadFile( hf, reloc_entries, count * sizeof(struct relocation_entry_s), &res, NULL) ||
189         (res != count * sizeof(struct relocation_entry_s)))
190     {
191         WARN_(fixup)("Unable to read relocation information\n" );
192         return FALSE;
193     }
194
195     /*
196      * Go through the relocation table one entry at a time.
197      */
198     rep = reloc_entries;
199     for (i = 0; i < count; i++, rep++)
200     {
201         /*
202          * Get the target address corresponding to this entry.
203          */
204
205         /* If additive, there is no target chain list. Instead, add source
206            and target */
207         additive = rep->relocation_type & NE_RELFLAG_ADDITIVE;
208         rep->relocation_type &= 0x3;
209
210         switch (rep->relocation_type)
211         {
212           case NE_RELTYPE_ORDINAL:
213             module = pModuleTable[rep->target1-1];
214             ordinal = rep->target2;
215             address = NE_GetEntryPoint( module, ordinal );
216             if (!address)
217             {
218                 NE_MODULE *pTarget = NE_GetPtr( module );
219                 if (!pTarget)
220                     WARN_(module)("Module not found: %04x, reference %d of module %*.*s\n",
221                              module, rep->target1, 
222                              *((BYTE *)pModule + pModule->name_table),
223                              *((BYTE *)pModule + pModule->name_table),
224                              (char *)pModule + pModule->name_table + 1 );
225                 else
226                 {
227                     ERR_(fixup)("No implementation for %.*s.%d, setting to 0xdeadbeef\n",
228                             *((BYTE *)pTarget + pTarget->name_table),
229                             (char *)pTarget + pTarget->name_table + 1,
230                             ordinal );
231                     address = (FARPROC16)0xdeadbeef;
232                 }
233             }
234             if (TRACE_ON(fixup))
235             {
236                 NE_MODULE *pTarget = NE_GetPtr( module );
237                 TRACE_(fixup)("%d: %.*s.%d=%04x:%04x %s\n", i + 1, 
238                        *((BYTE *)pTarget + pTarget->name_table),
239                        (char *)pTarget + pTarget->name_table + 1,
240                        ordinal, HIWORD(address), LOWORD(address),
241                        NE_GetRelocAddrName( rep->address_type, additive ) );
242             }
243             break;
244             
245           case NE_RELTYPE_NAME:
246             module = pModuleTable[rep->target1-1];
247             func_name = (char *)pModule + pModule->import_table + rep->target2;
248             memcpy( buffer, func_name+1, *func_name );
249             buffer[*func_name] = '\0';
250             func_name = buffer;
251             ordinal = NE_GetOrdinal( module, func_name );
252             address = NE_GetEntryPoint( module, ordinal );
253
254             if (ERR_ON(fixup) && !address)
255             {
256                 NE_MODULE *pTarget = NE_GetPtr( module );
257                 ERR_(fixup)("No implementation for %.*s.%s, setting to 0xdeadbeef\n",
258                     *((BYTE *)pTarget + pTarget->name_table),
259                     (char *)pTarget + pTarget->name_table + 1, func_name );
260             }
261             if (!address) address = (FARPROC16) 0xdeadbeef;
262             if (TRACE_ON(fixup))
263             {
264                 NE_MODULE *pTarget = NE_GetPtr( module );
265                 TRACE_(fixup)("%d: %.*s.%s=%04x:%04x %s\n", i + 1, 
266                        *((BYTE *)pTarget + pTarget->name_table),
267                        (char *)pTarget + pTarget->name_table + 1,
268                        func_name, HIWORD(address), LOWORD(address),
269                        NE_GetRelocAddrName( rep->address_type, additive ) );
270             }
271             break;
272             
273           case NE_RELTYPE_INTERNAL:
274             if ((rep->target1 & 0xff) == 0xff)
275             {
276                 address  = NE_GetEntryPoint( pModule->self, rep->target2 );
277             }
278             else
279             {
280                 address = (FARPROC16)PTR_SEG_OFF_TO_SEGPTR( SEL(pSegTable[rep->target1-1].hSeg), rep->target2 );
281             }
282             
283             TRACE_(fixup)("%d: %04x:%04x %s\n", 
284                    i + 1, HIWORD(address), LOWORD(address),
285                    NE_GetRelocAddrName( rep->address_type, additive ) );
286             break;
287
288           case NE_RELTYPE_OSFIXUP:
289             /* Relocation type 7:
290              *
291              *    These appear to be used as fixups for the Windows
292              * floating point emulator.  Let's just ignore them and
293              * try to use the hardware floating point.  Linux should
294              * successfully emulate the coprocessor if it doesn't
295              * exist.
296              */
297             TRACE_(fixup)("%d: TYPE %d, OFFSET %04x, TARGET %04x %04x %s\n",
298                    i + 1, rep->relocation_type, rep->offset,
299                    rep->target1, rep->target2,
300                    NE_GetRelocAddrName( rep->address_type, additive ) );
301             continue;
302         }
303
304         offset  = rep->offset;
305
306         /* Apparently, high bit of address_type is sometimes set; */
307         /* we ignore it for now */
308         if (rep->address_type > NE_RADDR_OFFSET32)
309         {
310             char module[10];
311             GetModuleName16( pModule->self, module, sizeof(module) );
312             ERR_(fixup)("WARNING: module %s: unknown reloc addr type = 0x%02x. Please report.\n",
313                  module, rep->address_type );
314         }
315
316         if (additive)
317         {
318             sp = PTR_SEG_OFF_TO_LIN( SEL(pSeg->hSeg), offset );
319             TRACE_(fixup)("    %04x:%04x\n", offset, *sp );
320             switch (rep->address_type & 0x7f)
321             {
322             case NE_RADDR_LOWBYTE:
323                 *(BYTE *)sp += LOBYTE((int)address);
324                 break;
325             case NE_RADDR_OFFSET16:
326                 *sp += LOWORD(address);
327                 break;
328             case NE_RADDR_POINTER32:
329                 *sp += LOWORD(address);
330                 *(sp+1) = HIWORD(address);
331                 break;
332             case NE_RADDR_SELECTOR:
333                 /* Borland creates additive records with offset zero. Strange, but OK */
334                 if (*sp)
335                     ERR_(fixup)("Additive selector to %04x.Please report\n",*sp);
336                 else
337                     *sp = HIWORD(address);
338                 break;
339             default:
340                 goto unknown;
341             }
342         }
343         else  /* non-additive fixup */
344         {
345             do
346             {
347                 sp = PTR_SEG_OFF_TO_LIN( SEL(pSeg->hSeg), offset );
348                 next_offset = *sp;
349                 TRACE_(fixup)("    %04x:%04x\n", offset, *sp );
350                 switch (rep->address_type & 0x7f)
351                 {
352                 case NE_RADDR_LOWBYTE:
353                     *(BYTE *)sp = LOBYTE((int)address);
354                     break;
355                 case NE_RADDR_OFFSET16:
356                     *sp = LOWORD(address);
357                     break;
358                 case NE_RADDR_POINTER32:
359                     *(FARPROC16 *)sp = address;
360                     break;
361                 case NE_RADDR_SELECTOR:
362                     *sp = SELECTOROF(address);
363                     break;
364                 default:
365                     goto unknown;
366                 }
367                 if (next_offset == offset) break;  /* avoid infinite loop */
368                 if (next_offset >= GlobalSize16(pSeg->hSeg)) break;
369                 offset = next_offset;
370             } while (offset != 0xffff);
371         }
372     }
373
374     HeapFree(GetProcessHeap(), 0, reloc_entries);
375     return TRUE;
376
377 unknown:
378     WARN_(fixup)("WARNING: %d: unknown ADDR TYPE %d,  "
379          "TYPE %d,  OFFSET %04x,  TARGET %04x %04x\n",
380          i + 1, rep->address_type, rep->relocation_type, 
381          rep->offset, rep->target1, rep->target2);
382     HeapFree(GetProcessHeap(), 0, reloc_entries);
383     return FALSE;
384 }
385
386
387 /***********************************************************************
388  *           NE_LoadAllSegments
389  */
390 BOOL NE_LoadAllSegments( NE_MODULE *pModule )
391 {
392     int i;
393     SEGTABLEENTRY * pSegTable = (SEGTABLEENTRY *) NE_SEG_TABLE(pModule);
394
395     if (pModule->flags & NE_FFLAGS_SELFLOAD)
396     {
397         HFILE hf;
398         HFILE16 hFile16;
399         /* Handle self-loading modules */
400         SELFLOADHEADER *selfloadheader;
401         HMODULE16 hselfload = GetModuleHandle16("WPROCS");
402         DWORD oldstack;
403
404         TRACE_(module)("%.*s is a self-loading module!\n",
405                      *((BYTE*)pModule + pModule->name_table),
406                      (char *)pModule + pModule->name_table + 1);
407         if (!NE_LoadSegment( pModule, 1 )) return FALSE;
408         selfloadheader = (SELFLOADHEADER *)
409                           PTR_SEG_OFF_TO_LIN(SEL(pSegTable->hSeg), 0);
410         selfloadheader->EntryAddrProc = NE_GetEntryPoint(hselfload,27);
411         selfloadheader->MyAlloc  = NE_GetEntryPoint(hselfload,28);
412         selfloadheader->SetOwner = NE_GetEntryPoint(GetModuleHandle16("KERNEL"),403);
413         pModule->self_loading_sel = SEL(GLOBAL_Alloc(GMEM_ZEROINIT, 0xFF00, pModule->self, FALSE, FALSE, FALSE));
414         oldstack = NtCurrentTeb()->cur_stack;
415         NtCurrentTeb()->cur_stack = PTR_SEG_OFF_TO_SEGPTR(pModule->self_loading_sel,
416                                                           0xff00 - sizeof(STACK16FRAME) );
417
418         DuplicateHandle( GetCurrentProcess(), NE_OpenFile(pModule),
419                          GetCurrentProcess(), &hf, 0, FALSE, DUPLICATE_SAME_ACCESS );
420         hFile16 = FILE_AllocDosHandle( hf );
421         TRACE_(dll)("CallBootAppProc(hModule=0x%04x,hf=0x%04x)\n",
422               pModule->self,hFile16);
423         Callbacks->CallBootAppProc(selfloadheader->BootApp, pModule->self,hFile16);
424         TRACE_(dll)("Return from CallBootAppProc\n");
425         _lclose16(hf);
426         NtCurrentTeb()->cur_stack = oldstack;
427
428         for (i = 2; i <= pModule->seg_count; i++)
429             if (!NE_LoadSegment( pModule, i )) return FALSE;
430     }
431     else
432     {
433         for (i = 1; i <= pModule->seg_count; i++)
434             if (!NE_LoadSegment( pModule, i )) return FALSE;
435     }
436     return TRUE;
437 }
438
439
440 /***********************************************************************
441  *           NE_FixupSegmentPrologs
442  *
443  * Fixup exported functions prologs of one segment
444  */
445 static void NE_FixupSegmentPrologs(NE_MODULE *pModule, WORD segnum)
446 {
447     SEGTABLEENTRY *pSegTable = NE_SEG_TABLE( pModule );
448     ET_BUNDLE *bundle;
449     ET_ENTRY *entry;
450     WORD dgroup, num_entries, sel = SEL(pSegTable[segnum-1].hSeg);
451     BYTE *pSeg, *pFunc;
452
453     TRACE_(module)("(%d);\n", segnum);
454
455     if (pSegTable[segnum-1].flags & NE_SEGFLAGS_DATA)
456     {
457         pSegTable[segnum-1].flags |= NE_SEGFLAGS_LOADED;
458         return;
459     }
460
461     if (!pModule->dgroup) return;
462
463     if (!(dgroup = SEL(pSegTable[pModule->dgroup-1].hSeg))) return;
464     
465     pSeg = PTR_SEG_OFF_TO_LIN(sel, 0);
466
467     bundle = (ET_BUNDLE *)((BYTE *)pModule+pModule->entry_table);
468
469     do {
470         TRACE_(module)("num_entries: %d, bundle: %p, next: %04x, pSeg: %p\n", bundle->last - bundle->first, bundle, bundle->next, pSeg);
471         if (!(num_entries = bundle->last - bundle->first))
472             return;
473         entry = (ET_ENTRY *)((BYTE *)bundle+6);
474         while (num_entries--)
475     {
476             /*TRACE(module, "entry: %p, entry->segnum: %d, entry->offs: %04x\n", entry, entry->segnum, entry->offs);*/
477             if (entry->segnum == segnum)
478         {
479                 pFunc = ((BYTE *)pSeg+entry->offs);
480                 TRACE_(module)("pFunc: %p, *(DWORD *)pFunc: %08lx, num_entries: %d\n", pFunc, *(DWORD *)pFunc, num_entries);
481                 if (*(pFunc+2) == 0x90)
482         {
483                     if (*(WORD *)pFunc == 0x581e) /* push ds, pop ax */
484                     {
485                         TRACE_(module)("patch %04x:%04x -> mov ax, ds\n", sel, entry->offs);
486                         *(WORD *)pFunc = 0xd88c; /* mov ax, ds */
487         }
488
489                     if (*(WORD *)pFunc == 0xd88c)
490                         {
491                         if ((entry->flags & 2)) /* public data ? */
492                         {
493                             TRACE_(module)("patch %04x:%04x -> mov ax, dgroup [%04x]\n", sel, entry->offs, dgroup);
494                             *pFunc = 0xb8; /* mov ax, */
495                             *(WORD *)(pFunc+1) = dgroup;
496                     }
497                     else
498                         if ((pModule->flags & NE_FFLAGS_MULTIPLEDATA)
499                         && (entry->flags & 1)) /* exported ? */
500                     {
501                             TRACE_(module)("patch %04x:%04x -> nop, nop\n", sel, entry->offs);
502                             *(WORD *)pFunc = 0x9090; /* nop, nop */
503                         }
504                     }
505                 }
506             }
507             entry++;
508         }
509     } while ( (bundle->next)
510          && (bundle = ((ET_BUNDLE *)((BYTE *)pModule + bundle->next))) );
511 }
512
513
514 /***********************************************************************
515  *           PatchCodeHandle
516  *
517  * Needed for self-loading modules.
518  */
519 DWORD WINAPI PatchCodeHandle16(HANDLE16 hSeg)
520 {
521     WORD segnum;
522     WORD sel = SEL(hSeg);
523     NE_MODULE *pModule = NE_GetPtr(FarGetOwner16(sel));
524     SEGTABLEENTRY *pSegTable = NE_SEG_TABLE(pModule);
525
526     TRACE_(module)("(%04x);\n", hSeg);
527
528     /* find the segment number of the module that belongs to hSeg */
529     for (segnum = 1; segnum <= pModule->seg_count; segnum++)
530     {
531         if (SEL(pSegTable[segnum-1].hSeg) == sel)
532         {
533             NE_FixupSegmentPrologs(pModule, segnum);
534             break;
535         }
536     }
537
538     return MAKELONG(hSeg, sel);
539 }
540
541
542 /***********************************************************************
543  *           NE_GetDLLInitParams
544  */
545 static VOID NE_GetDLLInitParams( NE_MODULE *pModule, 
546                                  WORD *hInst, WORD *ds, WORD *heap )
547 {
548     SEGTABLEENTRY *pSegTable = NE_SEG_TABLE( pModule );
549
550     if (!(pModule->flags & NE_FFLAGS_SINGLEDATA))
551     {
552         if (pModule->flags & NE_FFLAGS_MULTIPLEDATA || pModule->dgroup)
553         {
554             /* Not SINGLEDATA */
555             ERR_(dll)("Library is not marked SINGLEDATA\n");
556             exit(1);
557         }
558         else  /* DATA NONE DLL */
559         {
560             *ds = 0;
561             *heap = 0;
562         }
563     }
564     else  /* DATA SINGLE DLL */
565     {
566         if (pModule->dgroup) {
567             *ds   = SEL(pSegTable[pModule->dgroup-1].hSeg);
568             *heap = pModule->heap_size;
569         }
570         else /* hmm, DLL has no dgroup,
571                 but why has it NE_FFLAGS_SINGLEDATA set ?
572                 Buggy DLL compiler ? */
573         {
574             *ds   = 0;
575             *heap = 0;
576         }
577     }
578
579     *hInst = *ds ? GlobalHandle16(*ds) : pModule->self;
580 }
581
582
583 /***********************************************************************
584  *           NE_InitDLL
585  *
586  * Call the DLL initialization code
587  */
588 static BOOL NE_InitDLL( TDB* pTask, NE_MODULE *pModule )
589 {
590     SEGTABLEENTRY *pSegTable;
591     WORD hInst, ds, heap;
592     CONTEXT86 context;
593
594     pSegTable = NE_SEG_TABLE( pModule );
595
596     if (!(pModule->flags & NE_FFLAGS_LIBMODULE) ||
597         (pModule->flags & NE_FFLAGS_WIN32)) return TRUE; /*not a library*/
598
599     /* Call USER signal handler for Win3.1 compatibility. */
600     TASK_CallTaskSignalProc( USIG16_DLL_LOAD, pModule->self );
601
602     if (!pModule->cs) return TRUE;  /* no initialization code */
603
604
605     /* Registers at initialization must be:
606      * cx     heap size
607      * di     library instance
608      * ds     data segment if any
609      * es:si  command line (always 0)
610      */
611
612     memset( &context, 0, sizeof(context) );
613
614     NE_GetDLLInitParams( pModule, &hInst, &ds, &heap );
615
616     ECX_reg(&context) = heap;
617     EDI_reg(&context) = hInst;
618     DS_reg(&context)  = ds;
619     ES_reg(&context)  = ds;   /* who knows ... */
620
621     CS_reg(&context)  = SEL(pSegTable[pModule->cs-1].hSeg);
622     EIP_reg(&context) = pModule->ip;
623     EBP_reg(&context) = OFFSETOF(NtCurrentTeb()->cur_stack) + (WORD)&((STACK16FRAME*)0)->bp;
624
625
626     pModule->cs = 0;  /* Don't initialize it twice */
627     TRACE_(dll)("Calling LibMain, cs:ip=%04lx:%04lx ds=%04lx di=%04x cx=%04x\n", 
628                  CS_reg(&context), EIP_reg(&context), DS_reg(&context),
629                  DI_reg(&context), CX_reg(&context) );
630     Callbacks->CallRegisterShortProc( &context, 0 );
631     return TRUE;
632 }
633
634 /***********************************************************************
635  *           NE_InitializeDLLs
636  *
637  * Recursively initialize all DLLs (according to the order in which 
638  * they where loaded).
639  */
640 void NE_InitializeDLLs( HMODULE16 hModule )
641 {
642     TDB* pTask = (TDB*)GlobalLock16(GetCurrentTask());
643     NE_MODULE *pModule;
644     HMODULE16 *pDLL;
645
646     if (!(pModule = NE_GetPtr( hModule ))) return;
647     assert( !(pModule->flags & NE_FFLAGS_WIN32) );
648
649     if (pModule->dlls_to_init)
650     {
651         HGLOBAL16 to_init = pModule->dlls_to_init;
652         pModule->dlls_to_init = 0;
653         for (pDLL = (HMODULE16 *)GlobalLock16( to_init ); *pDLL; pDLL++)
654         {
655             NE_InitializeDLLs( *pDLL );
656         }
657         GlobalFree16( to_init );
658     }
659     NE_InitDLL( pTask, pModule );
660 }
661
662
663 /***********************************************************************
664  *           NE_CallDllEntryPoint
665  *
666  * Call the DllEntryPoint of DLLs with subsystem >= 4.0 
667  */
668 typedef DWORD WINAPI (*WinNEEntryProc)(DWORD,WORD,WORD,WORD,DWORD,WORD);
669
670 static void NE_CallDllEntryPoint( NE_MODULE *pModule, DWORD dwReason )
671 {
672     WORD hInst, ds, heap;
673     FARPROC16 entryPoint;
674     WORD ordinal;
675
676     if (!(pModule->flags & NE_FFLAGS_LIBMODULE)) return;
677     if (!(pModule->flags & NE_FFLAGS_BUILTIN) && pModule->expected_version < 0x0400) return;
678     if (!(ordinal = NE_GetOrdinal( pModule->self, "DllEntryPoint" ))) return;
679     if (!(entryPoint = NE_GetEntryPoint( pModule->self, ordinal ))) return;
680
681     NE_GetDLLInitParams( pModule, &hInst, &ds, &heap );
682
683     TRACE_(dll)( "Calling %s DllEntryPoint, cs:ip=%04x:%04x\n",
684                  NE_MODULE_NAME( pModule ),
685                  SELECTOROF(entryPoint), OFFSETOF(entryPoint) );
686
687     if ( pModule->flags & NE_FFLAGS_BUILTIN )
688     {
689         WinNEEntryProc entryProc = (WinNEEntryProc)
690                           ((ENTRYPOINT16 *)PTR_SEG_TO_LIN( entryPoint ))->target;
691
692         entryProc( dwReason, hInst, ds, heap, 0, 0 );
693     }
694     else
695     {
696         LPBYTE stack = (LPBYTE)CURRENT_STACK16;
697         CONTEXT86 context;
698
699         memset( &context, 0, sizeof(context) );
700         DS_reg(&context) = ds;
701         ES_reg(&context) = ds;   /* who knows ... */
702
703         CS_reg(&context) = HIWORD(entryPoint);
704         EIP_reg(&context) = LOWORD(entryPoint);
705         EBP_reg(&context) =  OFFSETOF( NtCurrentTeb()->cur_stack )
706                              + (WORD)&((STACK16FRAME*)0)->bp;
707
708         *(DWORD *)(stack -  4) = dwReason;      /* dwReason */
709         *(WORD *) (stack -  6) = hInst;         /* hInst */
710         *(WORD *) (stack -  8) = ds;            /* wDS */
711         *(WORD *) (stack - 10) = heap;          /* wHeapSize */
712         *(DWORD *)(stack - 14) = 0;             /* dwReserved1 */
713         *(WORD *) (stack - 16) = 0;             /* wReserved2 */
714
715         Callbacks->CallRegisterShortProc( &context, 16 );
716     }
717 }
718
719 /***********************************************************************
720  *           NE_DllProcessAttach
721  * 
722  * Call the DllEntryPoint of all modules this one (recursively)
723  * depends on, according to the order in which they were loaded.
724  *
725  * Note that --as opposed to the PE module case-- there is no notion
726  * of 'module loaded into a process' for NE modules, and hence we 
727  * have no place to store the fact that the DllEntryPoint of a
728  * given module was already called on behalf of this process (e.g.
729  * due to some earlier LoadLibrary16 call).
730  *
731  * Thus, we just call the DllEntryPoint twice in that case.  Win9x
732  * appears to behave this way as well ...
733  *
734  * This routine must only be called with the Win16Lock held.
735  * 
736  * FIXME:  We should actually abort loading in case the DllEntryPoint
737  *         returns FALSE ...
738  *
739  */
740 void NE_DllProcessAttach( HMODULE16 hModule )
741 {
742     NE_MODULE *pModule;
743     WORD *pModRef;
744     int i;
745
746     if (!(pModule = NE_GetPtr( hModule ))) return;
747     assert( !(pModule->flags & NE_FFLAGS_WIN32) );
748
749     /* Check for recursive call */
750     if ( pModule->misc_flags & 0x80 ) return;
751
752     TRACE_(dll)("(%s) - START\n", NE_MODULE_NAME(pModule) );
753
754     /* Tag current module to prevent recursive loop */
755     pModule->misc_flags |= 0x80;
756
757     /* Recursively attach all DLLs this one depends on */
758     pModRef = NE_MODULE_TABLE( pModule );
759     for ( i = 0; i < pModule->modref_count; i++ )
760         if ( pModRef[i] )
761             NE_DllProcessAttach( (HMODULE16)pModRef[i] );
762
763     /* Call DLL entry point */
764     NE_CallDllEntryPoint( pModule, DLL_PROCESS_ATTACH );
765
766     /* Remove recursion flag */
767     pModule->misc_flags &= ~0x80;
768
769     TRACE_(dll)("(%s) - END\n", NE_MODULE_NAME(pModule) );
770 }
771
772
773 /***********************************************************************
774  *           NE_Ne2MemFlags
775  *
776  * This function translates NE segment flags to GlobalAlloc flags
777  */
778 static WORD NE_Ne2MemFlags(WORD flags)
779
780     WORD memflags = 0;
781 #if 1
782     if (flags & NE_SEGFLAGS_DISCARDABLE) 
783       memflags |= GMEM_DISCARDABLE;
784     if (flags & NE_SEGFLAGS_MOVEABLE || 
785         ( ! (flags & NE_SEGFLAGS_DATA) &&
786           ! (flags & NE_SEGFLAGS_LOADED) &&
787           ! (flags & NE_SEGFLAGS_ALLOCATED)
788          )
789         )
790       memflags |= GMEM_MOVEABLE;
791     memflags |= GMEM_ZEROINIT;
792 #else
793     memflags = GMEM_ZEROINIT | GMEM_FIXED;
794 #endif
795     return memflags;
796 }
797
798 /***********************************************************************
799  *           NE_AllocateSegment (WPROCS.26)
800  *
801  * MyAlloc() function for self-loading apps.
802  */
803 DWORD WINAPI NE_AllocateSegment( WORD wFlags, WORD wSize, WORD wElem )
804 {
805     WORD size = wSize << wElem;
806     HANDLE16 hMem = 0;
807
808     if (wSize || (wFlags & NE_SEGFLAGS_MOVEABLE))
809         hMem = GlobalAlloc16( NE_Ne2MemFlags(wFlags), size);
810
811     if ( ((wFlags & 0x7) != 0x1) && /* DATA */
812          ((wFlags & 0x7) != 0x7) ) /* DATA|ALLOCATED|LOADED */
813     {
814         WORD hSel = SEL(hMem);
815         WORD access = SelectorAccessRights16(hSel,0,0);
816
817         access |= 2<<2; /* SEGMENT_CODE */
818         SelectorAccessRights16(hSel,1,access);
819     }
820     if (size)
821         return MAKELONG( hMem, SEL(hMem) );
822     else
823         return MAKELONG( 0, hMem );
824 }
825
826 /***********************************************************************
827  *           NE_GetInstance
828  */
829 HINSTANCE16 NE_GetInstance( NE_MODULE *pModule )
830 {
831     if ( !pModule->dgroup )
832         return pModule->self;
833     else
834     {
835         SEGTABLEENTRY *pSeg;
836         pSeg = NE_SEG_TABLE( pModule ) + pModule->dgroup - 1;
837         return pSeg->hSeg;
838     }
839 }    
840
841 /***********************************************************************
842  *           NE_CreateSegment
843  */
844 BOOL NE_CreateSegment( NE_MODULE *pModule, int segnum )
845 {
846     SEGTABLEENTRY *pSeg = NE_SEG_TABLE( pModule ) + segnum - 1;
847     int minsize;
848
849     assert( !(pModule->flags & NE_FFLAGS_WIN32) );
850
851     if ( segnum < 1 || segnum > pModule->seg_count )
852         return FALSE;
853
854     if ( (pModule->flags & NE_FFLAGS_SELFLOAD) && segnum != 1 )
855         return TRUE;    /* selfloader allocates segment itself */
856
857     if ( (pSeg->flags & NE_SEGFLAGS_ALLOCATED) && segnum != pModule->dgroup )
858         return TRUE;    /* all but DGROUP only allocated once */
859
860     minsize = pSeg->minsize ? pSeg->minsize : 0x10000;
861     if ( segnum == pModule->ss )     minsize += pModule->stack_size;
862     if ( segnum == pModule->dgroup ) minsize += pModule->heap_size;
863
864     pSeg->hSeg = GLOBAL_Alloc( NE_Ne2MemFlags(pSeg->flags),
865                                    minsize, pModule->self,
866                                    !(pSeg->flags & NE_SEGFLAGS_DATA),
867                                     (pSeg->flags & NE_SEGFLAGS_32BIT) != 0,
868                             FALSE /*pSeg->flags & NE_SEGFLAGS_READONLY*/ );
869     if (!pSeg->hSeg) return FALSE;
870
871     pSeg->flags |= NE_SEGFLAGS_ALLOCATED;
872     return TRUE;
873 }
874
875 /***********************************************************************
876  *           NE_CreateAllSegments
877  */
878 BOOL NE_CreateAllSegments( NE_MODULE *pModule )
879 {
880     int i;
881     for ( i = 1; i <= pModule->seg_count; i++ )
882         if ( !NE_CreateSegment( pModule, i ) )
883             return FALSE;
884
885     pModule->dgroup_entry = pModule->dgroup ? pModule->seg_table +
886                             (pModule->dgroup - 1) * sizeof(SEGTABLEENTRY) : 0;
887     return TRUE;
888 }
889
890
891 /**********************************************************************
892  *          IsSharedSelector    (KERNEL.345)
893  */
894 BOOL16 WINAPI IsSharedSelector16( HANDLE16 selector )
895 {
896     /* Check whether the selector belongs to a DLL */
897     NE_MODULE *pModule = NE_GetPtr( selector );
898     if (!pModule) return FALSE;
899     return (pModule->flags & NE_FFLAGS_LIBMODULE) != 0;
900 }