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