wined3d: Pass a wined3d_buffer structure to buffer_get_memory().
[wine] / dlls / krnl386.exe16 / 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 "kernel16_private.h"
41 #include "wine/debug.h"
42
43 WINE_DEFAULT_DEBUG_CHANNEL(fixup);
44 WINE_DECLARE_DEBUG_CHANNEL(dll);
45 WINE_DECLARE_DEBUG_CHANNEL(module);
46
47 /*
48  * Relocation table entry
49  */
50 struct relocation_entry_s
51 {
52     BYTE address_type;    /* Relocation address type */
53     BYTE relocation_type; /* Relocation type */
54     WORD offset;          /* Offset in segment to fixup */
55     WORD target1;         /* Target specification */
56     WORD target2;         /* Target specification */
57 };
58
59 /*
60  * Relocation address types
61  */
62 #define NE_RADDR_LOWBYTE      0
63 #define NE_RADDR_SELECTOR     2
64 #define NE_RADDR_POINTER32    3
65 #define NE_RADDR_OFFSET16     5
66 #define NE_RADDR_POINTER48    11
67 #define NE_RADDR_OFFSET32     13
68
69 /*
70  * Relocation types
71  */
72 #define NE_RELTYPE_INTERNAL  0
73 #define NE_RELTYPE_ORDINAL   1
74 #define NE_RELTYPE_NAME      2
75 #define NE_RELTYPE_OSFIXUP   3
76 #define NE_RELFLAG_ADDITIVE  4
77
78 /* Self-loading modules contain this structure in their first segment */
79 typedef struct
80 {
81     WORD      version;       /* Must be "A0" (0x3041) */
82     WORD      reserved;
83     FARPROC16 BootApp;       /* startup procedure */
84     FARPROC16 LoadAppSeg;    /* procedure to load a segment */
85     FARPROC16 reserved2;
86     FARPROC16 MyAlloc;       /* memory allocation procedure,
87                               * wine must write this field */
88     FARPROC16 EntryAddrProc;
89     FARPROC16 ExitProc;      /* exit procedure */
90     WORD      reserved3[4];
91     FARPROC16 SetOwner;      /* Set Owner procedure, exported by wine */
92 } SELFLOADHEADER;
93
94 #define SEL(x) ((x)|1)
95
96 static void NE_FixupSegmentPrologs(NE_MODULE *pModule, WORD segnum);
97
98
99 /***********************************************************************
100  *           NE_GetRelocAddrName
101  */
102 static const char *NE_GetRelocAddrName( BYTE addr_type, int additive )
103 {
104     switch(addr_type & 0x7f)
105     {
106     case NE_RADDR_LOWBYTE:   return additive ? "BYTE add" : "BYTE";
107     case NE_RADDR_OFFSET16:  return additive ? "OFFSET16 add" : "OFFSET16";
108     case NE_RADDR_POINTER32: return additive ? "POINTER32 add" : "POINTER32";
109     case NE_RADDR_SELECTOR:  return additive ? "SELECTOR add" : "SELECTOR";
110     case NE_RADDR_POINTER48: return additive ? "POINTER48 add" : "POINTER48";
111     case NE_RADDR_OFFSET32:  return additive ? "OFFSET32 add" : "OFFSET32";
112     }
113     return "???";
114 }
115
116
117 /***********************************************************************
118  *           NE_OpenFile
119  */
120 static HFILE16 NE_OpenFile( NE_MODULE *pModule )
121 {
122     char *name = NE_MODULE_NAME( pModule );
123     HANDLE handle = CreateFileA( name, GENERIC_READ, FILE_SHARE_READ,
124                                  NULL, OPEN_EXISTING, 0, 0 );
125
126     if (handle == INVALID_HANDLE_VALUE)
127     {
128         ERR( "Can't open file '%s' for module %04x\n", name, pModule->self );
129         return HFILE_ERROR;
130     }
131     return Win32HandleToDosFileHandle( handle );
132 }
133
134
135 /***********************************************************************
136  *           apply_relocations
137  *
138  * Apply relocations to a segment. Helper for NE_LoadSegment.
139  */
140 static inline BOOL apply_relocations( NE_MODULE *pModule, const struct relocation_entry_s *rep,
141                                       int count, int segnum )
142 {
143     BYTE *func_name;
144     char buffer[256];
145     int i, ordinal;
146     WORD offset, *sp;
147     HMODULE16 module;
148     FARPROC16 address = 0;
149     HMODULE16 *pModuleTable = (HMODULE16 *)((char *)pModule + pModule->ne_modtab);
150     SEGTABLEENTRY *pSegTable = NE_SEG_TABLE( pModule );
151     SEGTABLEENTRY *pSeg = pSegTable + segnum - 1;
152
153     /*
154      * Go through the relocation table one entry at a time.
155      */
156     for (i = 0; i < count; i++, rep++)
157     {
158         /*
159          * Get the target address corresponding to this entry.
160          */
161
162         /* If additive, there is no target chain list. Instead, add source
163            and target */
164         int additive = rep->relocation_type & NE_RELFLAG_ADDITIVE;
165         switch (rep->relocation_type & 3)
166         {
167         case NE_RELTYPE_ORDINAL:
168             module = pModuleTable[rep->target1-1];
169             ordinal = rep->target2;
170             address = NE_GetEntryPoint( module, ordinal );
171             if (!address)
172             {
173                 NE_MODULE *pTarget = NE_GetPtr( module );
174                 if (!pTarget)
175                     WARN_(module)("Module not found: %04x, reference %d of module %*.*s\n",
176                              module, rep->target1,
177                              *((BYTE *)pModule + pModule->ne_restab),
178                              *((BYTE *)pModule + pModule->ne_restab),
179                              (char *)pModule + pModule->ne_restab + 1 );
180                 else
181                 {
182                     ERR("No implementation for %.*s.%d, setting to 0xdeadbeef\n",
183                             *((BYTE *)pTarget + pTarget->ne_restab),
184                             (char *)pTarget + pTarget->ne_restab + 1,
185                             ordinal );
186                     address = (FARPROC16)0xdeadbeef;
187                 }
188             }
189             if (TRACE_ON(fixup))
190             {
191                 NE_MODULE *pTarget = NE_GetPtr( module );
192                 TRACE("%d: %.*s.%d=%04x:%04x %s\n", i + 1,
193                        *((BYTE *)pTarget + pTarget->ne_restab),
194                        (char *)pTarget + pTarget->ne_restab + 1,
195                        ordinal, HIWORD(address), LOWORD(address),
196                        NE_GetRelocAddrName( rep->address_type, additive ) );
197             }
198             break;
199
200         case NE_RELTYPE_NAME:
201             module = pModuleTable[rep->target1-1];
202             func_name = (BYTE *)pModule + pModule->ne_imptab + rep->target2;
203             memcpy( buffer, func_name+1, *func_name );
204             buffer[*func_name] = '\0';
205             ordinal = NE_GetOrdinal( module, buffer );
206             address = NE_GetEntryPoint( module, ordinal );
207
208             if (ERR_ON(fixup) && !address)
209             {
210                 NE_MODULE *pTarget = NE_GetPtr( module );
211                 ERR("No implementation for %.*s.%s, setting to 0xdeadbeef\n",
212                     *((BYTE *)pTarget + pTarget->ne_restab),
213                     (char *)pTarget + pTarget->ne_restab + 1, buffer );
214             }
215             if (!address) address = (FARPROC16) 0xdeadbeef;
216             if (TRACE_ON(fixup))
217             {
218                 NE_MODULE *pTarget = NE_GetPtr( module );
219                 TRACE("%d: %.*s.%s=%04x:%04x %s\n", i + 1,
220                        *((BYTE *)pTarget + pTarget->ne_restab),
221                        (char *)pTarget + pTarget->ne_restab + 1,
222                        buffer, HIWORD(address), LOWORD(address),
223                        NE_GetRelocAddrName( rep->address_type, additive ) );
224             }
225             break;
226
227         case NE_RELTYPE_INTERNAL:
228             if ((rep->target1 & 0xff) == 0xff)
229             {
230                 address  = NE_GetEntryPoint( pModule->self, rep->target2 );
231             }
232             else
233             {
234                 address = (FARPROC16)MAKESEGPTR( SEL(pSegTable[rep->target1-1].hSeg), rep->target2 );
235             }
236
237             TRACE("%d: %04x:%04x %s\n",
238                   i + 1, HIWORD(address), LOWORD(address),
239                   NE_GetRelocAddrName( rep->address_type, additive ) );
240             break;
241
242         case NE_RELTYPE_OSFIXUP:
243             /* Relocation type 7:
244              *
245              *    These appear to be used as fixups for the Windows
246              * floating point emulator.  Let's just ignore them and
247              * try to use the hardware floating point.  Linux should
248              * successfully emulate the coprocessor if it doesn't
249              * exist.
250              */
251             TRACE("%d: TYPE %d, OFFSET %04x, TARGET %04x %04x %s\n",
252                   i + 1, rep->relocation_type, rep->offset,
253                   rep->target1, rep->target2,
254                   NE_GetRelocAddrName( rep->address_type, additive ) );
255             continue;
256         }
257
258         offset  = rep->offset;
259
260         /* Apparently, high bit of address_type is sometimes set; */
261         /* we ignore it for now */
262         if (rep->address_type > NE_RADDR_OFFSET32)
263         {
264             char module[10];
265             GetModuleName16( pModule->self, module, sizeof(module) );
266             ERR("WARNING: module %s: unknown reloc addr type = 0x%02x. Please report.\n",
267                  module, rep->address_type );
268         }
269
270         if (additive)
271         {
272             sp = MapSL( MAKESEGPTR( SEL(pSeg->hSeg), offset ) );
273             TRACE("    %04x:%04x\n", offset, *sp );
274             switch (rep->address_type & 0x7f)
275             {
276             case NE_RADDR_LOWBYTE:
277                 *(BYTE *)sp += LOBYTE((int)address);
278                 break;
279             case NE_RADDR_OFFSET16:
280                 *sp += LOWORD(address);
281                 break;
282             case NE_RADDR_POINTER32:
283                 *sp += LOWORD(address);
284                 *(sp+1) = HIWORD(address);
285                 break;
286             case NE_RADDR_SELECTOR:
287                 /* Borland creates additive records with offset zero. Strange, but OK */
288                 if (*sp)
289                     ERR("Additive selector to %04x.Please report\n",*sp);
290                 else
291                     *sp = HIWORD(address);
292                 break;
293             default:
294                 goto unknown;
295             }
296         }
297         else  /* non-additive fixup */
298         {
299             do
300             {
301                 WORD next_offset;
302
303                 sp = MapSL( MAKESEGPTR( SEL(pSeg->hSeg), offset ) );
304                 next_offset = *sp;
305                 TRACE("    %04x:%04x\n", offset, *sp );
306                 switch (rep->address_type & 0x7f)
307                 {
308                 case NE_RADDR_LOWBYTE:
309                     *(BYTE *)sp = LOBYTE((int)address);
310                     break;
311                 case NE_RADDR_OFFSET16:
312                     *sp = LOWORD(address);
313                     break;
314                 case NE_RADDR_POINTER32:
315                     *(FARPROC16 *)sp = address;
316                     break;
317                 case NE_RADDR_SELECTOR:
318                     *sp = SELECTOROF(address);
319                     break;
320                 default:
321                     goto unknown;
322                 }
323                 if (next_offset == offset) break;  /* avoid infinite loop */
324                 if (next_offset >= GlobalSize16(pSeg->hSeg)) break;
325                 offset = next_offset;
326             } while (offset != 0xffff);
327         }
328     }
329     return TRUE;
330
331 unknown:
332     WARN("WARNING: %d: unknown ADDR TYPE %d,  "
333          "TYPE %d,  OFFSET %04x,  TARGET %04x %04x\n",
334          i + 1, rep->address_type, rep->relocation_type,
335          rep->offset, rep->target1, rep->target2);
336     return FALSE;
337 }
338
339
340 /***********************************************************************
341  *           NE_LoadSegment
342  */
343 BOOL NE_LoadSegment( NE_MODULE *pModule, WORD segnum )
344 {
345     WORD count;
346     DWORD pos;
347     const struct relocation_entry_s *rep;
348     int size;
349     SEGTABLEENTRY *pSegTable = NE_SEG_TABLE( pModule );
350     SEGTABLEENTRY *pSeg = pSegTable + segnum - 1;
351
352     if (pSeg->flags & NE_SEGFLAGS_LOADED)
353     {
354         /* self-loader ? -> already loaded it */
355         if (pModule->ne_flags & NE_FFLAGS_SELFLOAD)
356             return TRUE;
357
358         /* leave, except for DGROUP, as this may be the second instance */
359         if (segnum != pModule->ne_autodata)
360             return TRUE;
361     }
362
363     if (!pSeg->filepos) return TRUE;  /* No file image, just return */
364
365     TRACE_(module)("Loading segment %d, hSeg=%04x, flags=%04x\n",
366                     segnum, pSeg->hSeg, pSeg->flags );
367     pos = pSeg->filepos << pModule->ne_align;
368     if (pSeg->size) size = pSeg->size;
369     else size = pSeg->minsize ? pSeg->minsize : 0x10000;
370
371     if (pModule->ne_flags & NE_FFLAGS_SELFLOAD && segnum > 1)
372     {
373         /* Implement self-loading segments */
374         SELFLOADHEADER *selfloadheader;
375         void *oldstack;
376         HFILE16 hFile16;
377         WORD args[3];
378         DWORD ret;
379
380         selfloadheader = MapSL( MAKESEGPTR(SEL(pSegTable->hSeg),0) );
381         oldstack = NtCurrentTeb()->WOW32Reserved;
382         NtCurrentTeb()->WOW32Reserved = (void *)MAKESEGPTR(pModule->self_loading_sel,
383                                                            0xff00 - sizeof(STACK16FRAME));
384
385         hFile16 = NE_OpenFile( pModule );
386         TRACE_(dll)("Call LoadAppSegProc(hmodule=0x%04x,hf=%x,segnum=%d)\n",
387                     pModule->self,hFile16,segnum );
388         args[2] = pModule->self;
389         args[1] = hFile16;
390         args[0] = segnum;
391         WOWCallback16Ex( (DWORD)selfloadheader->LoadAppSeg, WCB16_PASCAL, sizeof(args), args, &ret );
392         pSeg->hSeg = LOWORD(ret);
393         TRACE_(dll)("Ret LoadAppSegProc: hSeg=0x%04x\n", pSeg->hSeg);
394         _lclose16( hFile16 );
395         NtCurrentTeb()->WOW32Reserved = oldstack;
396
397         pSeg->flags |= NE_SEGFLAGS_LOADED;
398         return TRUE;
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 = ((const short *)curr)[0];
423             unsigned int len =  ((const 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 = 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 = pSeg+entry->offs;
553                 TRACE("pFunc: %p, *(DWORD *)pFunc: %08x, 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 if ((pModule->ne_flags & NE_FFLAGS_MULTIPLEDATA)
571                                  && (entry->flags & 1)) /* exported ? */
572                         {
573                             TRACE("patch %04x:%04x -> nop, nop\n", sel, entry->offs);
574                             *(WORD *)pFunc = 0x9090; /* nop, nop */
575                         }
576                     }
577                 }
578             }
579             entry++;
580         }
581     } while ( (bundle->next) && (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->ne_cseg; 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->ne_flags & NE_FFLAGS_SINGLEDATA))
622     {
623         if (pModule->ne_flags & NE_FFLAGS_MULTIPLEDATA || pModule->ne_autodata)
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->ne_autodata) {
638             *ds   = SEL(pSegTable[pModule->ne_autodata-1].hSeg);
639             *heap = pModule->ne_heap;
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     CONTEXT context;
664
665     pSegTable = NE_SEG_TABLE( pModule );
666
667     if (!(pModule->ne_flags & NE_FFLAGS_LIBMODULE) ||
668         (pModule->ne_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 (!SELECTOROF(pModule->ne_csip)) 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[SELECTOROF(pModule->ne_csip)-1].hSeg);
694     context.Eip   = OFFSETOF(pModule->ne_csip);
695     context.Ebp   = OFFSETOF(NtCurrentTeb()->WOW32Reserved) + FIELD_OFFSET(STACK16FRAME,bp);
696
697     pModule->ne_csip = 0;  /* Don't initialize it twice */
698     TRACE_(dll)("Calling LibMain for %.*s, cs:ip=%04x:%04x ds=%04x di=%04x cx=%04x\n",
699                 *((BYTE*)pModule + pModule->ne_restab),
700                 (char *)pModule + pModule->ne_restab + 1,
701                 context.SegCs, context.Eip, context.SegDs,
702                 LOWORD(context.Edi), LOWORD(context.Ecx) );
703     WOWCallback16Ex( 0, WCB16_REGS, 0, NULL, (DWORD *)&context );
704     return TRUE;
705 }
706
707 /***********************************************************************
708  *           NE_InitializeDLLs
709  *
710  * Recursively initialize all DLLs (according to the order in which
711  * they where loaded).
712  */
713 void NE_InitializeDLLs( HMODULE16 hModule )
714 {
715     NE_MODULE *pModule;
716     HMODULE16 *pDLL;
717
718     if (!(pModule = NE_GetPtr( hModule ))) return;
719     assert( !(pModule->ne_flags & NE_FFLAGS_WIN32) );
720
721     if (pModule->dlls_to_init)
722     {
723         HGLOBAL16 to_init = pModule->dlls_to_init;
724         pModule->dlls_to_init = 0;
725         for (pDLL = GlobalLock16( to_init ); *pDLL; pDLL++)
726         {
727             NE_InitializeDLLs( *pDLL );
728         }
729         GlobalFree16( to_init );
730     }
731     NE_InitDLL( pModule );
732 }
733
734
735 /**********************************************************************
736  *          NE_CallUserSignalProc
737  *
738  * According to "Undocumented Windows", the task signal proc is
739  * bypassed for module load/unload notifications, and the USER signal
740  * proc is called directly instead. This is what this function does.
741  */
742 typedef DWORD (WINAPI *pSignalProc)( HANDLE16 module, UINT16 code, UINT16 exit,
743                                      HINSTANCE16 inst, HQUEUE16 queue );
744
745 void NE_CallUserSignalProc( HMODULE16 hModule, UINT16 code )
746 {
747     FARPROC16 proc;
748     HMODULE16 user = GetModuleHandle16("user.exe");
749
750     if (!user) return;
751     if ((proc = GetProcAddress16( user, "SignalProc" )))
752     {
753         /* USER is always a builtin dll */
754         pSignalProc sigproc = (pSignalProc)((ENTRYPOINT16 *)MapSL( (SEGPTR)proc ))->target;
755         sigproc( hModule, code, 0, 0, 0 );
756     }
757 }
758
759
760 /***********************************************************************
761  *           NE_CallDllEntryPoint
762  *
763  * Call the DllEntryPoint of DLLs with subsystem >= 4.0
764  */
765 typedef DWORD (WINAPI *WinNEEntryProc)(DWORD,WORD,WORD,WORD,DWORD,WORD);
766
767 static void NE_CallDllEntryPoint( NE_MODULE *pModule, DWORD dwReason )
768 {
769     WORD hInst, ds, heap;
770     FARPROC16 entryPoint;
771
772     if (!(pModule->ne_flags & NE_FFLAGS_LIBMODULE)) return;
773     if (!(pModule->ne_flags & NE_FFLAGS_BUILTIN) && pModule->ne_expver < 0x0400) return;
774     if (!(entryPoint = GetProcAddress16( pModule->self, "DllEntryPoint" ))) return;
775
776     NE_GetDLLInitParams( pModule, &hInst, &ds, &heap );
777
778     TRACE_(dll)( "Calling %s DllEntryPoint, cs:ip=%04x:%04x\n",
779                  NE_MODULE_NAME( pModule ),
780                  SELECTOROF(entryPoint), OFFSETOF(entryPoint) );
781
782     if ( pModule->ne_flags & NE_FFLAGS_BUILTIN )
783     {
784         WinNEEntryProc entryProc = (WinNEEntryProc)((ENTRYPOINT16 *)MapSL( (SEGPTR)entryPoint ))->target;
785
786         entryProc( dwReason, hInst, ds, heap, 0, 0 );
787     }
788     else
789     {
790         CONTEXT context;
791         WORD args[8];
792
793         memset( &context, 0, sizeof(context) );
794         context.SegDs = ds;
795         context.SegEs = ds;   /* who knows ... */
796         context.SegFs = wine_get_fs();
797         context.SegGs = wine_get_gs();
798         context.SegCs = HIWORD(entryPoint);
799         context.Eip   = LOWORD(entryPoint);
800         context.Ebp   = OFFSETOF(NtCurrentTeb()->WOW32Reserved) + FIELD_OFFSET(STACK16FRAME,bp);
801
802         args[7] = HIWORD(dwReason);
803         args[6] = LOWORD(dwReason);
804         args[5] = hInst;
805         args[4] = ds;
806         args[3] = heap;
807         args[2] = 0;     /* HIWORD(dwReserved1) */
808         args[1] = 0;     /* LOWORD(dwReserved1) */
809         args[0] = 0;     /* wReserved2 */
810         WOWCallback16Ex( 0, WCB16_REGS, sizeof(args), args, (DWORD *)&context );
811     }
812 }
813
814 /***********************************************************************
815  *           NE_DllProcessAttach
816  *
817  * Call the DllEntryPoint of all modules this one (recursively)
818  * depends on, according to the order in which they were loaded.
819  *
820  * Note that --as opposed to the PE module case-- there is no notion
821  * of 'module loaded into a process' for NE modules, and hence we
822  * have no place to store the fact that the DllEntryPoint of a
823  * given module was already called on behalf of this process (e.g.
824  * due to some earlier LoadLibrary16 call).
825  *
826  * Thus, we just call the DllEntryPoint twice in that case.  Win9x
827  * appears to behave this way as well ...
828  *
829  * This routine must only be called with the Win16Lock held.
830  *
831  * FIXME:  We should actually abort loading in case the DllEntryPoint
832  *         returns FALSE ...
833  *
834  */
835
836 struct ne_init_list
837 {
838     int count;
839     int size;
840     NE_MODULE **module;
841 };
842
843 static void add_to_init_list( struct ne_init_list *list, NE_MODULE *hModule )
844 {
845     NE_MODULE **newModule = NULL;
846     if ( list->count == list->size )
847     {
848         int newSize = list->size + 128;
849
850         if (list->module) 
851             newModule = HeapReAlloc( GetProcessHeap(), 0,
852                                              list->module, newSize*sizeof(NE_MODULE *) );
853         else
854             newModule = HeapAlloc( GetProcessHeap(), 0,
855                                              newSize*sizeof(NE_MODULE *) );
856         if ( !newModule )
857         {
858             FIXME_(dll)("Out of memory!\n");
859             return;
860         }
861
862         list->module = newModule;
863         list->size   = newSize;
864     }
865
866     list->module[list->count++] = hModule;
867 }
868
869 static void free_init_list( struct ne_init_list *list )
870 {
871     if ( list->module )
872     {
873         HeapFree( GetProcessHeap(), 0, list->module );
874         memset( list, 0, sizeof(*list) );
875     }
876 }
877
878 static void fill_init_list( struct ne_init_list *list, HMODULE16 hModule )
879 {
880     NE_MODULE *pModule;
881     HMODULE16 *pModRef;
882     int i;
883
884     if (!(pModule = NE_GetPtr( hModule ))) return;
885     assert( !(pModule->ne_flags & NE_FFLAGS_WIN32) );
886
887     /* Never add a module twice */
888     for ( i = 0; i < list->count; i++ )
889         if ( list->module[i] == pModule )
890             return;
891
892     /* Check for recursive call */
893     if ( pModule->ne_flagsothers & 0x80 ) return;
894
895     TRACE_(dll)("(%s) - START\n", NE_MODULE_NAME(pModule) );
896
897     /* Tag current module to prevent recursive loop */
898     pModule->ne_flagsothers |= 0x80;
899
900     /* Recursively attach all DLLs this one depends on */
901     pModRef = (HMODULE16 *)((char *)pModule + pModule->ne_modtab);
902     for ( i = 0; i < pModule->ne_cmod; i++ )
903         if ( pModRef[i] ) fill_init_list( list, pModRef[i] );
904
905     /* Add current module */
906     add_to_init_list( list, pModule );
907
908     /* Remove recursion flag */
909     pModule->ne_flagsothers &= ~0x80;
910
911     TRACE_(dll)("(%s) - END\n", NE_MODULE_NAME(pModule) );
912 }
913
914 static void call_init_list( struct ne_init_list *list )
915 {
916     int i;
917     for ( i = 0; i < list->count; i++ )
918         NE_CallDllEntryPoint( list->module[i], DLL_PROCESS_ATTACH );
919 }
920
921 void NE_DllProcessAttach( HMODULE16 hModule )
922 {
923     struct ne_init_list list;
924     memset( &list, 0, sizeof(list) );
925
926     fill_init_list( &list, hModule );
927     call_init_list( &list );
928     free_init_list( &list );
929 }
930
931
932 /***********************************************************************
933  *           NE_Ne2MemFlags
934  *
935  * This function translates NE segment flags to GlobalAlloc flags
936  */
937 static WORD NE_Ne2MemFlags(WORD flags)
938 {
939     WORD memflags = 0;
940 #if 1
941     if (flags & NE_SEGFLAGS_DISCARDABLE)
942       memflags |= GMEM_DISCARDABLE;
943     if (flags & NE_SEGFLAGS_MOVEABLE ||
944         ( ! (flags & NE_SEGFLAGS_DATA) &&
945           ! (flags & NE_SEGFLAGS_LOADED) &&
946           ! (flags & NE_SEGFLAGS_ALLOCATED)
947          )
948         )
949       memflags |= GMEM_MOVEABLE;
950     memflags |= GMEM_ZEROINIT;
951 #else
952     memflags = GMEM_ZEROINIT | GMEM_FIXED;
953 #endif
954     return memflags;
955 }
956
957 /***********************************************************************
958  *           MyAlloc   (KERNEL.668) Wine-specific export
959  *
960  * MyAlloc() function for self-loading apps.
961  */
962 DWORD WINAPI MyAlloc16( WORD wFlags, WORD wSize, WORD wElem )
963 {
964     WORD size = wSize << wElem;
965     HANDLE16 hMem = 0;
966
967     if (wSize || (wFlags & NE_SEGFLAGS_MOVEABLE))
968         hMem = GlobalAlloc16( NE_Ne2MemFlags(wFlags), size);
969
970     if ( ((wFlags & 0x7) != 0x1) && /* DATA */
971          ((wFlags & 0x7) != 0x7) ) /* DATA|ALLOCATED|LOADED */
972     {
973         WORD hSel = SEL(hMem);
974         WORD access = SelectorAccessRights16(hSel,0,0);
975
976         access |= 2<<2; /* SEGMENT_CODE */
977         SelectorAccessRights16(hSel,1,access);
978     }
979     if (size)
980         return MAKELONG( hMem, SEL(hMem) );
981     else
982         return MAKELONG( 0, hMem );
983 }
984
985 /***********************************************************************
986  *           NE_GetInstance
987  */
988 HINSTANCE16 NE_GetInstance( NE_MODULE *pModule )
989 {
990     if ( !pModule->ne_autodata )
991         return pModule->self;
992     else
993     {
994         SEGTABLEENTRY *pSeg;
995         pSeg = NE_SEG_TABLE( pModule ) + pModule->ne_autodata - 1;
996         return pSeg->hSeg;
997     }
998 }
999
1000 /***********************************************************************
1001  *           NE_CreateSegment
1002  */
1003 BOOL NE_CreateSegment( NE_MODULE *pModule, int segnum )
1004 {
1005     SEGTABLEENTRY *pSeg = NE_SEG_TABLE( pModule ) + segnum - 1;
1006     int minsize;
1007     unsigned char selflags;
1008
1009     assert( !(pModule->ne_flags & NE_FFLAGS_WIN32) );
1010
1011     if ( segnum < 1 || segnum > pModule->ne_cseg )
1012         return FALSE;
1013
1014     if ( (pModule->ne_flags & NE_FFLAGS_SELFLOAD) && segnum != 1 )
1015         return TRUE;    /* selfloader allocates segment itself */
1016
1017     if ( (pSeg->flags & NE_SEGFLAGS_ALLOCATED) && segnum != pModule->ne_autodata )
1018         return TRUE;    /* all but DGROUP only allocated once */
1019
1020     minsize = pSeg->minsize ? pSeg->minsize : 0x10000;
1021     if ( segnum == SELECTOROF(pModule->ne_sssp) ) minsize += pModule->ne_stack;
1022     if ( segnum == pModule->ne_autodata ) minsize += pModule->ne_heap;
1023
1024     selflags = (pSeg->flags & NE_SEGFLAGS_DATA) ? WINE_LDT_FLAGS_DATA : WINE_LDT_FLAGS_CODE;
1025     if (pSeg->flags & NE_SEGFLAGS_32BIT) selflags |= WINE_LDT_FLAGS_32BIT;
1026     pSeg->hSeg = GLOBAL_Alloc( NE_Ne2MemFlags(pSeg->flags), minsize, pModule->self, selflags );
1027     if (!pSeg->hSeg) return FALSE;
1028
1029     pSeg->flags |= NE_SEGFLAGS_ALLOCATED;
1030     return TRUE;
1031 }
1032
1033 /***********************************************************************
1034  *           NE_CreateAllSegments
1035  */
1036 BOOL NE_CreateAllSegments( NE_MODULE *pModule )
1037 {
1038     int i;
1039     for ( i = 1; i <= pModule->ne_cseg; i++ )
1040         if ( !NE_CreateSegment( pModule, i ) )
1041             return FALSE;
1042
1043     pModule->dgroup_entry = pModule->ne_autodata ? pModule->ne_segtab +
1044                             (pModule->ne_autodata - 1) * sizeof(SEGTABLEENTRY) : 0;
1045     return TRUE;
1046 }
1047
1048
1049 /**********************************************************************
1050  *          IsSharedSelector    (KERNEL.345)
1051  */
1052 BOOL16 WINAPI IsSharedSelector16( HANDLE16 selector )
1053 {
1054     /* Check whether the selector belongs to a DLL */
1055     NE_MODULE *pModule = NE_GetPtr( selector );
1056     if (!pModule) return FALSE;
1057     return (pModule->ne_flags & NE_FFLAGS_LIBMODULE) != 0;
1058 }