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