Don't allocate a glyphset until we actually need it.
[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 for %.*s, cs:ip=%04lx:%04lx ds=%04lx di=%04x cx=%04x\n",
698                 *((BYTE*)pModule + pModule->name_table),
699                 (char *)pModule + pModule->name_table + 1,
700                 context.SegCs, context.Eip, context.SegDs,
701                 LOWORD(context.Edi), LOWORD(context.Ecx) );
702     WOWCallback16Ex( 0, WCB16_REGS, 0, NULL, (DWORD *)&context );
703     return TRUE;
704 }
705
706 /***********************************************************************
707  *           NE_InitializeDLLs
708  *
709  * Recursively initialize all DLLs (according to the order in which
710  * they where loaded).
711  */
712 void NE_InitializeDLLs( HMODULE16 hModule )
713 {
714     NE_MODULE *pModule;
715     HMODULE16 *pDLL;
716
717     if (!(pModule = NE_GetPtr( hModule ))) return;
718     assert( !(pModule->flags & NE_FFLAGS_WIN32) );
719
720     if (pModule->dlls_to_init)
721     {
722         HGLOBAL16 to_init = pModule->dlls_to_init;
723         pModule->dlls_to_init = 0;
724         for (pDLL = (HMODULE16 *)GlobalLock16( to_init ); *pDLL; pDLL++)
725         {
726             NE_InitializeDLLs( *pDLL );
727         }
728         GlobalFree16( to_init );
729     }
730     NE_InitDLL( pModule );
731 }
732
733
734 /**********************************************************************
735  *          NE_CallUserSignalProc
736  *
737  * According to "Undocumented Windows", the task signal proc is
738  * bypassed for module load/unload notifications, and the USER signal
739  * proc is called directly instead. This is what this function does.
740  */
741 typedef DWORD (WINAPI *pSignalProc)( HANDLE16 module, UINT16 code, UINT16 exit,
742                                      HINSTANCE16 inst, HQUEUE16 queue );
743
744 void NE_CallUserSignalProc( HMODULE16 hModule, UINT16 code )
745 {
746     FARPROC16 proc;
747     HMODULE16 user = GetModuleHandle16("user.exe");
748
749     if (!user) return;
750     if ((proc = GetProcAddress16( user, "SignalProc" )))
751     {
752         /* USER is always a builtin dll */
753         pSignalProc sigproc = (pSignalProc)((ENTRYPOINT16 *)MapSL( (SEGPTR)proc ))->target;
754         sigproc( hModule, code, 0, 0, 0 );
755     }
756 }
757
758
759 /***********************************************************************
760  *           NE_CallDllEntryPoint
761  *
762  * Call the DllEntryPoint of DLLs with subsystem >= 4.0
763  */
764 typedef DWORD (WINAPI *WinNEEntryProc)(DWORD,WORD,WORD,WORD,DWORD,WORD);
765
766 static void NE_CallDllEntryPoint( NE_MODULE *pModule, DWORD dwReason )
767 {
768     WORD hInst, ds, heap;
769     FARPROC16 entryPoint;
770
771     if (!(pModule->flags & NE_FFLAGS_LIBMODULE)) return;
772     if (!(pModule->flags & NE_FFLAGS_BUILTIN) && pModule->expected_version < 0x0400) return;
773     if (!(entryPoint = GetProcAddress16( pModule->self, "DllEntryPoint" ))) return;
774
775     NE_GetDLLInitParams( pModule, &hInst, &ds, &heap );
776
777     TRACE_(dll)( "Calling %s DllEntryPoint, cs:ip=%04x:%04x\n",
778                  NE_MODULE_NAME( pModule ),
779                  SELECTOROF(entryPoint), OFFSETOF(entryPoint) );
780
781     if ( pModule->flags & NE_FFLAGS_BUILTIN )
782     {
783         WinNEEntryProc entryProc = (WinNEEntryProc)((ENTRYPOINT16 *)MapSL( (SEGPTR)entryPoint ))->target;
784
785         entryProc( dwReason, hInst, ds, heap, 0, 0 );
786     }
787     else
788     {
789         CONTEXT86 context;
790         WORD args[8];
791
792         memset( &context, 0, sizeof(context) );
793         context.SegDs = ds;
794         context.SegEs = ds;   /* who knows ... */
795         context.SegFs = wine_get_fs();
796         context.SegGs = wine_get_gs();
797         context.SegCs = HIWORD(entryPoint);
798         context.Eip = LOWORD(entryPoint);
799         context.Ebp =  OFFSETOF( NtCurrentTeb()->cur_stack )
800                              + (WORD)&((STACK16FRAME*)0)->bp;
801
802         args[7] = HIWORD(dwReason);
803         args[6] = LOWORD(dwReason);
804         args[5] = hInst;
805         args[4] = ds;
806         args[3] = heap;
807         args[2] = 0;     /* HIWORD(dwReserved1) */
808         args[1] = 0;     /* LOWORD(dwReserved1) */
809         args[0] = 0;     /* wReserved2 */
810         WOWCallback16Ex( 0, WCB16_REGS, sizeof(args), args, (DWORD *)&context );
811     }
812 }
813
814 /***********************************************************************
815  *           NE_DllProcessAttach
816  *
817  * Call the DllEntryPoint of all modules this one (recursively)
818  * depends on, according to the order in which they were loaded.
819  *
820  * Note that --as opposed to the PE module case-- there is no notion
821  * of 'module loaded into a process' for NE modules, and hence we
822  * have no place to store the fact that the DllEntryPoint of a
823  * given module was already called on behalf of this process (e.g.
824  * due to some earlier LoadLibrary16 call).
825  *
826  * Thus, we just call the DllEntryPoint twice in that case.  Win9x
827  * appears to behave this way as well ...
828  *
829  * This routine must only be called with the Win16Lock held.
830  *
831  * FIXME:  We should actually abort loading in case the DllEntryPoint
832  *         returns FALSE ...
833  *
834  */
835
836 struct ne_init_list
837 {
838     int count;
839     int size;
840     NE_MODULE **module;
841 };
842
843 static void add_to_init_list( struct ne_init_list *list, NE_MODULE *hModule )
844 {
845     NE_MODULE **newModule = NULL;
846     if ( list->count == list->size )
847     {
848         int newSize = list->size + 128;
849
850         if (list->module) 
851             newModule = HeapReAlloc( GetProcessHeap(), 0,
852                                              list->module, newSize*sizeof(NE_MODULE *) );
853         else
854             newModule = HeapAlloc( GetProcessHeap(), 0,
855                                              newSize*sizeof(NE_MODULE *) );
856         if ( !newModule )
857         {
858             FIXME_(dll)("Out of memory!\n");
859             return;
860         }
861
862         list->module = newModule;
863         list->size   = newSize;
864     }
865
866     list->module[list->count++] = hModule;
867 }
868
869 static void free_init_list( struct ne_init_list *list )
870 {
871     if ( list->module )
872     {
873         HeapFree( GetProcessHeap(), 0, list->module );
874         memset( list, 0, sizeof(*list) );
875     }
876 }
877
878 static void fill_init_list( struct ne_init_list *list, HMODULE16 hModule )
879 {
880     NE_MODULE *pModule;
881     HMODULE16 *pModRef;
882     int i;
883
884     if (!(pModule = NE_GetPtr( hModule ))) return;
885     assert( !(pModule->flags & NE_FFLAGS_WIN32) );
886
887     /* Never add a module twice */
888     for ( i = 0; i < list->count; i++ )
889         if ( list->module[i] == pModule )
890             return;
891
892     /* Check for recursive call */
893     if ( pModule->misc_flags & 0x80 ) return;
894
895     TRACE_(dll)("(%s) - START\n", NE_MODULE_NAME(pModule) );
896
897     /* Tag current module to prevent recursive loop */
898     pModule->misc_flags |= 0x80;
899
900     /* Recursively attach all DLLs this one depends on */
901     pModRef = (HMODULE16 *)((char *)pModule + pModule->modref_table);
902     for ( i = 0; i < pModule->modref_count; i++ )
903         if ( pModRef[i] ) fill_init_list( list, pModRef[i] );
904
905     /* Add current module */
906     add_to_init_list( list, pModule );
907
908     /* Remove recursion flag */
909     pModule->misc_flags &= ~0x80;
910
911     TRACE_(dll)("(%s) - END\n", NE_MODULE_NAME(pModule) );
912 }
913
914 static void call_init_list( struct ne_init_list *list )
915 {
916     int i;
917     for ( i = 0; i < list->count; i++ )
918         NE_CallDllEntryPoint( list->module[i], DLL_PROCESS_ATTACH );
919 }
920
921 void NE_DllProcessAttach( HMODULE16 hModule )
922 {
923     struct ne_init_list list;
924     memset( &list, 0, sizeof(list) );
925
926     fill_init_list( &list, hModule );
927     call_init_list( &list );
928     free_init_list( &list );
929 }
930
931
932 /***********************************************************************
933  *           NE_Ne2MemFlags
934  *
935  * This function translates NE segment flags to GlobalAlloc flags
936  */
937 static WORD NE_Ne2MemFlags(WORD flags)
938 {
939     WORD memflags = 0;
940 #if 1
941     if (flags & NE_SEGFLAGS_DISCARDABLE)
942       memflags |= GMEM_DISCARDABLE;
943     if (flags & NE_SEGFLAGS_MOVEABLE ||
944         ( ! (flags & NE_SEGFLAGS_DATA) &&
945           ! (flags & NE_SEGFLAGS_LOADED) &&
946           ! (flags & NE_SEGFLAGS_ALLOCATED)
947          )
948         )
949       memflags |= GMEM_MOVEABLE;
950     memflags |= GMEM_ZEROINIT;
951 #else
952     memflags = GMEM_ZEROINIT | GMEM_FIXED;
953 #endif
954     return memflags;
955 }
956
957 /***********************************************************************
958  *           MyAlloc   (KERNEL.668) Wine-specific export
959  *
960  * MyAlloc() function for self-loading apps.
961  */
962 DWORD WINAPI MyAlloc16( WORD wFlags, WORD wSize, WORD wElem )
963 {
964     WORD size = wSize << wElem;
965     HANDLE16 hMem = 0;
966
967     if (wSize || (wFlags & NE_SEGFLAGS_MOVEABLE))
968         hMem = GlobalAlloc16( NE_Ne2MemFlags(wFlags), size);
969
970     if ( ((wFlags & 0x7) != 0x1) && /* DATA */
971          ((wFlags & 0x7) != 0x7) ) /* DATA|ALLOCATED|LOADED */
972     {
973         WORD hSel = SEL(hMem);
974         WORD access = SelectorAccessRights16(hSel,0,0);
975
976         access |= 2<<2; /* SEGMENT_CODE */
977         SelectorAccessRights16(hSel,1,access);
978     }
979     if (size)
980         return MAKELONG( hMem, SEL(hMem) );
981     else
982         return MAKELONG( 0, hMem );
983 }
984
985 /***********************************************************************
986  *           NE_GetInstance
987  */
988 HINSTANCE16 NE_GetInstance( NE_MODULE *pModule )
989 {
990     if ( !pModule->dgroup )
991         return pModule->self;
992     else
993     {
994         SEGTABLEENTRY *pSeg;
995         pSeg = NE_SEG_TABLE( pModule ) + pModule->dgroup - 1;
996         return pSeg->hSeg;
997     }
998 }
999
1000 /***********************************************************************
1001  *           NE_CreateSegment
1002  */
1003 BOOL NE_CreateSegment( NE_MODULE *pModule, int segnum )
1004 {
1005     SEGTABLEENTRY *pSeg = NE_SEG_TABLE( pModule ) + segnum - 1;
1006     int minsize;
1007     unsigned char selflags;
1008
1009     assert( !(pModule->flags & NE_FFLAGS_WIN32) );
1010
1011     if ( segnum < 1 || segnum > pModule->seg_count )
1012         return FALSE;
1013
1014     if ( (pModule->flags & NE_FFLAGS_SELFLOAD) && segnum != 1 )
1015         return TRUE;    /* selfloader allocates segment itself */
1016
1017     if ( (pSeg->flags & NE_SEGFLAGS_ALLOCATED) && segnum != pModule->dgroup )
1018         return TRUE;    /* all but DGROUP only allocated once */
1019
1020     minsize = pSeg->minsize ? pSeg->minsize : 0x10000;
1021     if ( segnum == pModule->ss )     minsize += pModule->stack_size;
1022     if ( segnum == pModule->dgroup ) minsize += pModule->heap_size;
1023
1024     selflags = (pSeg->flags & NE_SEGFLAGS_DATA) ? WINE_LDT_FLAGS_DATA : WINE_LDT_FLAGS_CODE;
1025     if (pSeg->flags & NE_SEGFLAGS_32BIT) selflags |= WINE_LDT_FLAGS_32BIT;
1026     pSeg->hSeg = GLOBAL_Alloc( NE_Ne2MemFlags(pSeg->flags), minsize, pModule->self, selflags );
1027     if (!pSeg->hSeg) return FALSE;
1028
1029     pSeg->flags |= NE_SEGFLAGS_ALLOCATED;
1030     return TRUE;
1031 }
1032
1033 /***********************************************************************
1034  *           NE_CreateAllSegments
1035  */
1036 BOOL NE_CreateAllSegments( NE_MODULE *pModule )
1037 {
1038     int i;
1039     for ( i = 1; i <= pModule->seg_count; i++ )
1040         if ( !NE_CreateSegment( pModule, i ) )
1041             return FALSE;
1042
1043     pModule->dgroup_entry = pModule->dgroup ? pModule->seg_table +
1044                             (pModule->dgroup - 1) * sizeof(SEGTABLEENTRY) : 0;
1045     return TRUE;
1046 }
1047
1048
1049 /**********************************************************************
1050  *          IsSharedSelector    (KERNEL.345)
1051  */
1052 BOOL16 WINAPI IsSharedSelector16( HANDLE16 selector )
1053 {
1054     /* Check whether the selector belongs to a DLL */
1055     NE_MODULE *pModule = NE_GetPtr( selector );
1056     if (!pModule) return FALSE;
1057     return (pModule->flags & NE_FFLAGS_LIBMODULE) != 0;
1058 }