oleaut32: Add a test for loading/saving an empty picture.
[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 (!pSegTable[pModule->ne_autodata-1].hSeg) return;
537     dgroup = SEL(pSegTable[pModule->ne_autodata-1].hSeg);
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     CONTEXT 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         CONTEXT 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 }