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