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