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