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