Fixed Win16 documentation not fixed because of a bug in winapi_check.
[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 <sys/stat.h>
13 #include <fcntl.h>
14 #include <unistd.h>
15 #include <ctype.h>
16 #include <string.h>
17
18 #include "wine/winbase16.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 = (SELFLOADHEADER *)
145                 PTR_SEG_OFF_TO_LIN(SEL(pSegTable->hSeg),0);
146         oldstack = NtCurrentTeb()->cur_stack;
147         NtCurrentTeb()->cur_stack = PTR_SEG_OFF_TO_SEGPTR(pModule->self_loading_sel,
148                                                           0xff00 - sizeof(STACK16FRAME));
149
150         TRACE_(dll)("CallLoadAppSegProc(hmodule=0x%04x,hf=0x%04x,segnum=%d\n",
151                 pModule->self,hf,segnum );
152         DuplicateHandle( GetCurrentProcess(), hf, GetCurrentProcess(), &hFile32,
153                          0, FALSE, DUPLICATE_SAME_ACCESS );
154         hFile16 = Win32HandleToDosFileHandle( hFile32 );
155         pSeg->hSeg = NE_CallTo16_word_www( selfloadheader->LoadAppSeg,
156                                            pModule->self, hFile16, segnum );
157         TRACE_(dll)("Ret CallLoadAppSegProc: hSeg = 0x%04x\n", pSeg->hSeg);
158         _lclose16( hFile16 );
159         NtCurrentTeb()->cur_stack = oldstack;
160     }
161     else if (!(pSeg->flags & NE_SEGFLAGS_ITERATED))
162         ReadFile(hf, mem, size, &res, NULL);
163     else {
164       /*
165          The following bit of code for "iterated segments" was written without
166          any documentation on the format of these segments. It seems to work,
167          but may be missing something. If you have any doc please either send
168          it to me or fix the code yourself. gfm@werple.mira.net.au
169       */
170       char* buff = HeapAlloc(GetProcessHeap(), 0, size);
171       char* curr = buff;
172
173       if(buff == NULL) {
174           WARN_(dll)("Memory exausted!");
175           return FALSE;
176       }
177
178       ReadFile(hf, buff, size, &res, NULL);
179       while(curr < buff + size) {
180         unsigned int rept = *((short*) curr)++;
181         unsigned int len = *((short*) curr)++;
182         for(; rept > 0; rept--) {
183           char* bytes = curr;
184           unsigned int byte;
185           for(byte = 0; byte < len; byte++)
186             *mem++ = *bytes++;
187         }
188         curr += len;
189       }
190       HeapFree(GetProcessHeap(), 0, buff);
191     }
192
193     pSeg->flags |= NE_SEGFLAGS_LOADED;
194
195     /* Perform exported function prolog fixups */
196     NE_FixupSegmentPrologs( pModule, segnum );
197
198     if (!(pSeg->flags & NE_SEGFLAGS_RELOC_DATA))
199         return TRUE;  /* No relocation data, we are done */
200
201     ReadFile(hf, &count, sizeof(count), &res, NULL);
202     if (!count) return TRUE;
203
204     TRACE_(fixup)("Fixups for %.*s, segment %d, hSeg %04x\n",
205                    *((BYTE *)pModule + pModule->name_table),
206                    (char *)pModule + pModule->name_table + 1,
207                    segnum, pSeg->hSeg );
208     TRACE_(segment)("Fixups for %.*s, segment %d, hSeg %04x\n",
209                    *((BYTE *)pModule + pModule->name_table),
210                    (char *)pModule + pModule->name_table + 1,
211                    segnum, pSeg->hSeg );
212
213     reloc_entries = (struct relocation_entry_s *)HeapAlloc(GetProcessHeap(), 0, count * sizeof(struct relocation_entry_s));
214     if(reloc_entries == NULL) {
215         WARN_(fixup)("Not enough memory for relocation entries!");
216         return FALSE;
217     }
218     if (!ReadFile( hf, reloc_entries, count * sizeof(struct relocation_entry_s), &res, NULL) ||
219         (res != count * sizeof(struct relocation_entry_s)))
220     {
221         WARN_(fixup)("Unable to read relocation information\n" );
222         return FALSE;
223     }
224
225     /*
226      * Go through the relocation table one entry at a time.
227      */
228     rep = reloc_entries;
229     for (i = 0; i < count; i++, rep++)
230     {
231         /*
232          * Get the target address corresponding to this entry.
233          */
234
235         /* If additive, there is no target chain list. Instead, add source
236            and target */
237         additive = rep->relocation_type & NE_RELFLAG_ADDITIVE;
238         rep->relocation_type &= 0x3;
239
240         switch (rep->relocation_type)
241         {
242           case NE_RELTYPE_ORDINAL:
243             module = pModuleTable[rep->target1-1];
244             ordinal = rep->target2;
245             address = NE_GetEntryPoint( module, ordinal );
246             if (!address)
247             {
248                 NE_MODULE *pTarget = NE_GetPtr( module );
249                 if (!pTarget)
250                     WARN_(module)("Module not found: %04x, reference %d of module %*.*s\n",
251                              module, rep->target1, 
252                              *((BYTE *)pModule + pModule->name_table),
253                              *((BYTE *)pModule + pModule->name_table),
254                              (char *)pModule + pModule->name_table + 1 );
255                 else
256                 {
257                     ERR_(fixup)("No implementation for %.*s.%d, setting to 0xdeadbeef\n",
258                             *((BYTE *)pTarget + pTarget->name_table),
259                             (char *)pTarget + pTarget->name_table + 1,
260                             ordinal );
261                     address = (FARPROC16)0xdeadbeef;
262                 }
263             }
264             if (TRACE_ON(fixup))
265             {
266                 NE_MODULE *pTarget = NE_GetPtr( module );
267                 TRACE_(fixup)("%d: %.*s.%d=%04x:%04x %s\n", i + 1, 
268                        *((BYTE *)pTarget + pTarget->name_table),
269                        (char *)pTarget + pTarget->name_table + 1,
270                        ordinal, HIWORD(address), LOWORD(address),
271                        NE_GetRelocAddrName( rep->address_type, additive ) );
272             }
273             break;
274             
275           case NE_RELTYPE_NAME:
276             module = pModuleTable[rep->target1-1];
277             func_name = (char *)pModule + pModule->import_table + rep->target2;
278             memcpy( buffer, func_name+1, *func_name );
279             buffer[*func_name] = '\0';
280             func_name = buffer;
281             ordinal = NE_GetOrdinal( module, func_name );
282             address = NE_GetEntryPoint( module, ordinal );
283
284             if (ERR_ON(fixup) && !address)
285             {
286                 NE_MODULE *pTarget = NE_GetPtr( module );
287                 ERR_(fixup)("No implementation for %.*s.%s, setting to 0xdeadbeef\n",
288                     *((BYTE *)pTarget + pTarget->name_table),
289                     (char *)pTarget + pTarget->name_table + 1, func_name );
290             }
291             if (!address) address = (FARPROC16) 0xdeadbeef;
292             if (TRACE_ON(fixup))
293             {
294                 NE_MODULE *pTarget = NE_GetPtr( module );
295                 TRACE_(fixup)("%d: %.*s.%s=%04x:%04x %s\n", i + 1, 
296                        *((BYTE *)pTarget + pTarget->name_table),
297                        (char *)pTarget + pTarget->name_table + 1,
298                        func_name, HIWORD(address), LOWORD(address),
299                        NE_GetRelocAddrName( rep->address_type, additive ) );
300             }
301             break;
302             
303           case NE_RELTYPE_INTERNAL:
304             if ((rep->target1 & 0xff) == 0xff)
305             {
306                 address  = NE_GetEntryPoint( pModule->self, rep->target2 );
307             }
308             else
309             {
310                 address = (FARPROC16)PTR_SEG_OFF_TO_SEGPTR( SEL(pSegTable[rep->target1-1].hSeg), rep->target2 );
311             }
312             
313             TRACE_(fixup)("%d: %04x:%04x %s\n", 
314                    i + 1, HIWORD(address), LOWORD(address),
315                    NE_GetRelocAddrName( rep->address_type, additive ) );
316             break;
317
318           case NE_RELTYPE_OSFIXUP:
319             /* Relocation type 7:
320              *
321              *    These appear to be used as fixups for the Windows
322              * floating point emulator.  Let's just ignore them and
323              * try to use the hardware floating point.  Linux should
324              * successfully emulate the coprocessor if it doesn't
325              * exist.
326              */
327             TRACE_(fixup)("%d: TYPE %d, OFFSET %04x, TARGET %04x %04x %s\n",
328                    i + 1, rep->relocation_type, rep->offset,
329                    rep->target1, rep->target2,
330                    NE_GetRelocAddrName( rep->address_type, additive ) );
331             continue;
332         }
333
334         offset  = rep->offset;
335
336         /* Apparently, high bit of address_type is sometimes set; */
337         /* we ignore it for now */
338         if (rep->address_type > NE_RADDR_OFFSET32)
339         {
340             char module[10];
341             GetModuleName16( pModule->self, module, sizeof(module) );
342             ERR_(fixup)("WARNING: module %s: unknown reloc addr type = 0x%02x. Please report.\n",
343                  module, rep->address_type );
344         }
345
346         if (additive)
347         {
348             sp = PTR_SEG_OFF_TO_LIN( SEL(pSeg->hSeg), offset );
349             TRACE_(fixup)("    %04x:%04x\n", offset, *sp );
350             switch (rep->address_type & 0x7f)
351             {
352             case NE_RADDR_LOWBYTE:
353                 *(BYTE *)sp += LOBYTE((int)address);
354                 break;
355             case NE_RADDR_OFFSET16:
356                 *sp += LOWORD(address);
357                 break;
358             case NE_RADDR_POINTER32:
359                 *sp += LOWORD(address);
360                 *(sp+1) = HIWORD(address);
361                 break;
362             case NE_RADDR_SELECTOR:
363                 /* Borland creates additive records with offset zero. Strange, but OK */
364                 if (*sp)
365                     ERR_(fixup)("Additive selector to %04x.Please report\n",*sp);
366                 else
367                     *sp = HIWORD(address);
368                 break;
369             default:
370                 goto unknown;
371             }
372         }
373         else  /* non-additive fixup */
374         {
375             do
376             {
377                 sp = PTR_SEG_OFF_TO_LIN( SEL(pSeg->hSeg), offset );
378                 next_offset = *sp;
379                 TRACE_(fixup)("    %04x:%04x\n", offset, *sp );
380                 switch (rep->address_type & 0x7f)
381                 {
382                 case NE_RADDR_LOWBYTE:
383                     *(BYTE *)sp = LOBYTE((int)address);
384                     break;
385                 case NE_RADDR_OFFSET16:
386                     *sp = LOWORD(address);
387                     break;
388                 case NE_RADDR_POINTER32:
389                     *(FARPROC16 *)sp = address;
390                     break;
391                 case NE_RADDR_SELECTOR:
392                     *sp = SELECTOROF(address);
393                     break;
394                 default:
395                     goto unknown;
396                 }
397                 if (next_offset == offset) break;  /* avoid infinite loop */
398                 if (next_offset >= GlobalSize16(pSeg->hSeg)) break;
399                 offset = next_offset;
400             } while (offset != 0xffff);
401         }
402     }
403
404     HeapFree(GetProcessHeap(), 0, reloc_entries);
405     return TRUE;
406
407 unknown:
408     WARN_(fixup)("WARNING: %d: unknown ADDR TYPE %d,  "
409          "TYPE %d,  OFFSET %04x,  TARGET %04x %04x\n",
410          i + 1, rep->address_type, rep->relocation_type, 
411          rep->offset, rep->target1, rep->target2);
412     HeapFree(GetProcessHeap(), 0, reloc_entries);
413     return FALSE;
414 }
415
416
417 /***********************************************************************
418  *           NE_LoadAllSegments
419  */
420 BOOL NE_LoadAllSegments( NE_MODULE *pModule )
421 {
422     int i;
423     SEGTABLEENTRY * pSegTable = (SEGTABLEENTRY *) NE_SEG_TABLE(pModule);
424
425     if (pModule->flags & NE_FFLAGS_SELFLOAD)
426     {
427         HFILE hf;
428         HFILE16 hFile16;
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 = (SELFLOADHEADER *)
439                           PTR_SEG_OFF_TO_LIN(SEL(pSegTable->hSeg), 0);
440         selfloadheader->EntryAddrProc = GetProcAddress16(mod,"EntryAddrProc");
441         selfloadheader->MyAlloc       = GetProcAddress16(mod,"MyAlloc");
442         selfloadheader->SetOwner      = GetProcAddress16(mod,"FarSetOwner");
443         pModule->self_loading_sel = SEL(GLOBAL_Alloc(GMEM_ZEROINIT, 0xFF00, pModule->self, WINE_LDT_FLAGS_DATA));
444         oldstack = NtCurrentTeb()->cur_stack;
445         NtCurrentTeb()->cur_stack = PTR_SEG_OFF_TO_SEGPTR(pModule->self_loading_sel,
446                                                           0xff00 - sizeof(STACK16FRAME) );
447
448         DuplicateHandle( GetCurrentProcess(), NE_OpenFile(pModule),
449                          GetCurrentProcess(), &hf, 0, FALSE, DUPLICATE_SAME_ACCESS );
450         hFile16 = Win32HandleToDosFileHandle( hf );
451         TRACE_(dll)("CallBootAppProc(hModule=0x%04x,hf=0x%04x)\n",
452               pModule->self,hFile16);
453         NE_CallTo16_word_ww(selfloadheader->BootApp, pModule->self,hFile16);
454         TRACE_(dll)("Return from CallBootAppProc\n");
455         _lclose16(hf);
456         NtCurrentTeb()->cur_stack = oldstack;
457
458         for (i = 2; i <= pModule->seg_count; i++)
459             if (!NE_LoadSegment( pModule, i )) return FALSE;
460     }
461     else
462     {
463         for (i = 1; i <= pModule->seg_count; i++)
464             if (!NE_LoadSegment( pModule, i )) return FALSE;
465     }
466     return TRUE;
467 }
468
469
470 /***********************************************************************
471  *           NE_FixupSegmentPrologs
472  *
473  * Fixup exported functions prologs of one segment
474  */
475 static void NE_FixupSegmentPrologs(NE_MODULE *pModule, WORD segnum)
476 {
477     SEGTABLEENTRY *pSegTable = NE_SEG_TABLE( pModule );
478     ET_BUNDLE *bundle;
479     ET_ENTRY *entry;
480     WORD dgroup, num_entries, sel = SEL(pSegTable[segnum-1].hSeg);
481     BYTE *pSeg, *pFunc;
482
483     TRACE_(module)("(%d);\n", segnum);
484
485     if (pSegTable[segnum-1].flags & NE_SEGFLAGS_DATA)
486     {
487         pSegTable[segnum-1].flags |= NE_SEGFLAGS_LOADED;
488         return;
489     }
490
491     if (!pModule->dgroup) return;
492
493     if (!(dgroup = SEL(pSegTable[pModule->dgroup-1].hSeg))) return;
494     
495     pSeg = PTR_SEG_OFF_TO_LIN(sel, 0);
496
497     bundle = (ET_BUNDLE *)((BYTE *)pModule+pModule->entry_table);
498
499     do {
500         TRACE_(module)("num_entries: %d, bundle: %p, next: %04x, pSeg: %p\n", bundle->last - bundle->first, bundle, bundle->next, pSeg);
501         if (!(num_entries = bundle->last - bundle->first))
502             return;
503         entry = (ET_ENTRY *)((BYTE *)bundle+6);
504         while (num_entries--)
505     {
506             /*TRACE_(module)("entry: %p, entry->segnum: %d, entry->offs: %04x\n", entry, entry->segnum, entry->offs);*/
507             if (entry->segnum == segnum)
508         {
509                 pFunc = ((BYTE *)pSeg+entry->offs);
510                 TRACE_(module)("pFunc: %p, *(DWORD *)pFunc: %08lx, num_entries: %d\n", pFunc, *(DWORD *)pFunc, num_entries);
511                 if (*(pFunc+2) == 0x90)
512         {
513                     if (*(WORD *)pFunc == 0x581e) /* push ds, pop ax */
514                     {
515                         TRACE_(module)("patch %04x:%04x -> mov ax, ds\n", sel, entry->offs);
516                         *(WORD *)pFunc = 0xd88c; /* mov ax, ds */
517         }
518
519                     if (*(WORD *)pFunc == 0xd88c)
520                         {
521                         if ((entry->flags & 2)) /* public data ? */
522                         {
523                             TRACE_(module)("patch %04x:%04x -> mov ax, dgroup [%04x]\n", sel, entry->offs, dgroup);
524                             *pFunc = 0xb8; /* mov ax, */
525                             *(WORD *)(pFunc+1) = dgroup;
526                     }
527                     else
528                         if ((pModule->flags & NE_FFLAGS_MULTIPLEDATA)
529                         && (entry->flags & 1)) /* exported ? */
530                     {
531                             TRACE_(module)("patch %04x:%04x -> nop, nop\n", sel, entry->offs);
532                             *(WORD *)pFunc = 0x9090; /* nop, nop */
533                         }
534                     }
535                 }
536             }
537             entry++;
538         }
539     } while ( (bundle->next)
540          && (bundle = ((ET_BUNDLE *)((BYTE *)pModule + bundle->next))) );
541 }
542
543
544 /***********************************************************************
545  *           PatchCodeHandle
546  *
547  * Needed for self-loading modules.
548  */
549 DWORD WINAPI PatchCodeHandle16(HANDLE16 hSeg)
550 {
551     WORD segnum;
552     WORD sel = SEL(hSeg);
553     NE_MODULE *pModule = NE_GetPtr(FarGetOwner16(sel));
554     SEGTABLEENTRY *pSegTable = NE_SEG_TABLE(pModule);
555
556     TRACE_(module)("(%04x);\n", hSeg);
557
558     /* find the segment number of the module that belongs to hSeg */
559     for (segnum = 1; segnum <= pModule->seg_count; segnum++)
560     {
561         if (SEL(pSegTable[segnum-1].hSeg) == sel)
562         {
563             NE_FixupSegmentPrologs(pModule, segnum);
564             break;
565         }
566     }
567
568     return MAKELONG(hSeg, sel);
569 }
570
571
572 /***********************************************************************
573  *           NE_GetDLLInitParams
574  */
575 static VOID NE_GetDLLInitParams( NE_MODULE *pModule, 
576                                  WORD *hInst, WORD *ds, WORD *heap )
577 {
578     SEGTABLEENTRY *pSegTable = NE_SEG_TABLE( pModule );
579
580     if (!(pModule->flags & NE_FFLAGS_SINGLEDATA))
581     {
582         if (pModule->flags & NE_FFLAGS_MULTIPLEDATA || pModule->dgroup)
583         {
584             /* Not SINGLEDATA */
585             ERR_(dll)("Library is not marked SINGLEDATA\n");
586             exit(1);
587         }
588         else  /* DATA NONE DLL */
589         {
590             *ds = 0;
591             *heap = 0;
592         }
593     }
594     else  /* DATA SINGLE DLL */
595     {
596         if (pModule->dgroup) {
597             *ds   = SEL(pSegTable[pModule->dgroup-1].hSeg);
598             *heap = pModule->heap_size;
599         }
600         else /* hmm, DLL has no dgroup,
601                 but why has it NE_FFLAGS_SINGLEDATA set ?
602                 Buggy DLL compiler ? */
603         {
604             *ds   = 0;
605             *heap = 0;
606         }
607     }
608
609     *hInst = *ds ? GlobalHandle16(*ds) : pModule->self;
610 }
611
612
613 /***********************************************************************
614  *           NE_InitDLL
615  *
616  * Call the DLL initialization code
617  */
618 static BOOL NE_InitDLL( TDB* pTask, NE_MODULE *pModule )
619 {
620     SEGTABLEENTRY *pSegTable;
621     WORD hInst, ds, heap;
622     CONTEXT86 context;
623
624     pSegTable = NE_SEG_TABLE( pModule );
625
626     if (!(pModule->flags & NE_FFLAGS_LIBMODULE) ||
627         (pModule->flags & NE_FFLAGS_WIN32)) return TRUE; /*not a library*/
628
629     /* Call USER signal handler for Win3.1 compatibility. */
630     TASK_CallTaskSignalProc( USIG16_DLL_LOAD, pModule->self );
631
632     if (!pModule->cs) return TRUE;  /* no initialization code */
633
634
635     /* Registers at initialization must be:
636      * cx     heap size
637      * di     library instance
638      * ds     data segment if any
639      * es:si  command line (always 0)
640      */
641
642     memset( &context, 0, sizeof(context) );
643
644     NE_GetDLLInitParams( pModule, &hInst, &ds, &heap );
645
646     context.Ecx = heap;
647     context.Edi = hInst;
648     context.SegDs  = ds;
649     context.SegEs  = ds;   /* who knows ... */
650
651     context.SegCs  = SEL(pSegTable[pModule->cs-1].hSeg);
652     context.Eip = pModule->ip;
653     context.Ebp = OFFSETOF(NtCurrentTeb()->cur_stack) + (WORD)&((STACK16FRAME*)0)->bp;
654
655
656     pModule->cs = 0;  /* Don't initialize it twice */
657     TRACE_(dll)("Calling LibMain, cs:ip=%04lx:%04lx ds=%04lx di=%04x cx=%04x\n", 
658                  context.SegCs, context.Eip, context.SegDs,
659                  LOWORD(context.Edi), LOWORD(context.Ecx) );
660     wine_call_to_16_regs_short( &context, 0 );
661     return TRUE;
662 }
663
664 /***********************************************************************
665  *           NE_InitializeDLLs
666  *
667  * Recursively initialize all DLLs (according to the order in which 
668  * they where loaded).
669  */
670 void NE_InitializeDLLs( HMODULE16 hModule )
671 {
672     TDB* pTask = (TDB*)GlobalLock16(GetCurrentTask());
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( pTask, 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)
718                           ((ENTRYPOINT16 *)PTR_SEG_TO_LIN( entryPoint ))->target;
719
720         entryProc( dwReason, hInst, ds, heap, 0, 0 );
721     }
722     else
723     {
724         LPBYTE stack = (LPBYTE)CURRENT_STACK16;
725         CONTEXT86 context;
726
727         memset( &context, 0, sizeof(context) );
728         context.SegDs = ds;
729         context.SegEs = ds;   /* who knows ... */
730
731         context.SegCs = HIWORD(entryPoint);
732         context.Eip = LOWORD(entryPoint);
733         context.Ebp =  OFFSETOF( NtCurrentTeb()->cur_stack )
734                              + (WORD)&((STACK16FRAME*)0)->bp;
735
736         *(DWORD *)(stack -  4) = dwReason;      /* dwReason */
737         *(WORD *) (stack -  6) = hInst;         /* hInst */
738         *(WORD *) (stack -  8) = ds;            /* wDS */
739         *(WORD *) (stack - 10) = heap;          /* wHeapSize */
740         *(DWORD *)(stack - 14) = 0;             /* dwReserved1 */
741         *(WORD *) (stack - 16) = 0;             /* wReserved2 */
742
743         wine_call_to_16_regs_short( &context, 16 );
744     }
745 }
746
747 /***********************************************************************
748  *           NE_DllProcessAttach
749  * 
750  * Call the DllEntryPoint of all modules this one (recursively)
751  * depends on, according to the order in which they were loaded.
752  *
753  * Note that --as opposed to the PE module case-- there is no notion
754  * of 'module loaded into a process' for NE modules, and hence we 
755  * have no place to store the fact that the DllEntryPoint of a
756  * given module was already called on behalf of this process (e.g.
757  * due to some earlier LoadLibrary16 call).
758  *
759  * Thus, we just call the DllEntryPoint twice in that case.  Win9x
760  * appears to behave this way as well ...
761  *
762  * This routine must only be called with the Win16Lock held.
763  * 
764  * FIXME:  We should actually abort loading in case the DllEntryPoint
765  *         returns FALSE ...
766  *
767  */
768 void NE_DllProcessAttach( HMODULE16 hModule )
769 {
770     NE_MODULE *pModule;
771     WORD *pModRef;
772     int i;
773
774     if (!(pModule = NE_GetPtr( hModule ))) return;
775     assert( !(pModule->flags & NE_FFLAGS_WIN32) );
776
777     /* Check for recursive call */
778     if ( pModule->misc_flags & 0x80 ) return;
779
780     TRACE_(dll)("(%s) - START\n", NE_MODULE_NAME(pModule) );
781
782     /* Tag current module to prevent recursive loop */
783     pModule->misc_flags |= 0x80;
784
785     /* Recursively attach all DLLs this one depends on */
786     pModRef = NE_MODULE_TABLE( pModule );
787     for ( i = 0; i < pModule->modref_count; i++ )
788         if ( pModRef[i] )
789             NE_DllProcessAttach( (HMODULE16)pModRef[i] );
790
791     /* Call DLL entry point */
792     NE_CallDllEntryPoint( pModule, DLL_PROCESS_ATTACH );
793
794     /* Remove recursion flag */
795     pModule->misc_flags &= ~0x80;
796
797     TRACE_(dll)("(%s) - END\n", NE_MODULE_NAME(pModule) );
798 }
799
800
801 /***********************************************************************
802  *           NE_Ne2MemFlags
803  *
804  * This function translates NE segment flags to GlobalAlloc flags
805  */
806 static WORD NE_Ne2MemFlags(WORD flags)
807
808     WORD memflags = 0;
809 #if 1
810     if (flags & NE_SEGFLAGS_DISCARDABLE) 
811       memflags |= GMEM_DISCARDABLE;
812     if (flags & NE_SEGFLAGS_MOVEABLE || 
813         ( ! (flags & NE_SEGFLAGS_DATA) &&
814           ! (flags & NE_SEGFLAGS_LOADED) &&
815           ! (flags & NE_SEGFLAGS_ALLOCATED)
816          )
817         )
818       memflags |= GMEM_MOVEABLE;
819     memflags |= GMEM_ZEROINIT;
820 #else
821     memflags = GMEM_ZEROINIT | GMEM_FIXED;
822 #endif
823     return memflags;
824 }
825
826 /***********************************************************************
827  *           MyAlloc16   (KERNEL Wine-specific export)
828  *
829  * MyAlloc() function for self-loading apps.
830  */
831 DWORD WINAPI MyAlloc16( WORD wFlags, WORD wSize, WORD wElem )
832 {
833     WORD size = wSize << wElem;
834     HANDLE16 hMem = 0;
835
836     if (wSize || (wFlags & NE_SEGFLAGS_MOVEABLE))
837         hMem = GlobalAlloc16( NE_Ne2MemFlags(wFlags), size);
838
839     if ( ((wFlags & 0x7) != 0x1) && /* DATA */
840          ((wFlags & 0x7) != 0x7) ) /* DATA|ALLOCATED|LOADED */
841     {
842         WORD hSel = SEL(hMem);
843         WORD access = SelectorAccessRights16(hSel,0,0);
844
845         access |= 2<<2; /* SEGMENT_CODE */
846         SelectorAccessRights16(hSel,1,access);
847     }
848     if (size)
849         return MAKELONG( hMem, SEL(hMem) );
850     else
851         return MAKELONG( 0, hMem );
852 }
853
854 /***********************************************************************
855  *           NE_GetInstance
856  */
857 HINSTANCE16 NE_GetInstance( NE_MODULE *pModule )
858 {
859     if ( !pModule->dgroup )
860         return pModule->self;
861     else
862     {
863         SEGTABLEENTRY *pSeg;
864         pSeg = NE_SEG_TABLE( pModule ) + pModule->dgroup - 1;
865         return pSeg->hSeg;
866     }
867 }    
868
869 /***********************************************************************
870  *           NE_CreateSegment
871  */
872 BOOL NE_CreateSegment( NE_MODULE *pModule, int segnum )
873 {
874     SEGTABLEENTRY *pSeg = NE_SEG_TABLE( pModule ) + segnum - 1;
875     int minsize;
876     unsigned char selflags;
877
878     assert( !(pModule->flags & NE_FFLAGS_WIN32) );
879
880     if ( segnum < 1 || segnum > pModule->seg_count )
881         return FALSE;
882
883     if ( (pModule->flags & NE_FFLAGS_SELFLOAD) && segnum != 1 )
884         return TRUE;    /* selfloader allocates segment itself */
885
886     if ( (pSeg->flags & NE_SEGFLAGS_ALLOCATED) && segnum != pModule->dgroup )
887         return TRUE;    /* all but DGROUP only allocated once */
888
889     minsize = pSeg->minsize ? pSeg->minsize : 0x10000;
890     if ( segnum == pModule->ss )     minsize += pModule->stack_size;
891     if ( segnum == pModule->dgroup ) minsize += pModule->heap_size;
892
893     selflags = (pSeg->flags & NE_SEGFLAGS_DATA) ? WINE_LDT_FLAGS_DATA : WINE_LDT_FLAGS_CODE;
894     if (pSeg->flags & NE_SEGFLAGS_32BIT) selflags |= WINE_LDT_FLAGS_32BIT;
895     pSeg->hSeg = GLOBAL_Alloc( NE_Ne2MemFlags(pSeg->flags), minsize, pModule->self, selflags );
896     if (!pSeg->hSeg) return FALSE;
897
898     pSeg->flags |= NE_SEGFLAGS_ALLOCATED;
899     return TRUE;
900 }
901
902 /***********************************************************************
903  *           NE_CreateAllSegments
904  */
905 BOOL NE_CreateAllSegments( NE_MODULE *pModule )
906 {
907     int i;
908     for ( i = 1; i <= pModule->seg_count; i++ )
909         if ( !NE_CreateSegment( pModule, i ) )
910             return FALSE;
911
912     pModule->dgroup_entry = pModule->dgroup ? pModule->seg_table +
913                             (pModule->dgroup - 1) * sizeof(SEGTABLEENTRY) : 0;
914     return TRUE;
915 }
916
917
918 /**********************************************************************
919  *          IsSharedSelector    (KERNEL.345)
920  */
921 BOOL16 WINAPI IsSharedSelector16( HANDLE16 selector )
922 {
923     /* Check whether the selector belongs to a DLL */
924     NE_MODULE *pModule = NE_GetPtr( selector );
925     if (!pModule) return FALSE;
926     return (pModule->flags & NE_FFLAGS_LIBMODULE) != 0;
927 }