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