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