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