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