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