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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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 "winternl.h"
39 #include "wine/library.h"
40 #include "kernel_private.h"
41 #include "kernel16_private.h"
42 #include "toolhelp.h"
43 #include "wine/debug.h"
44
45 WINE_DEFAULT_DEBUG_CHANNEL(fixup);
46 WINE_DECLARE_DEBUG_CHANNEL(dll);
47 WINE_DECLARE_DEBUG_CHANNEL(module);
48
49 /*
50 * Relocation table entry
51 */
52 struct relocation_entry_s
53 {
54 BYTE address_type; /* Relocation address type */
55 BYTE relocation_type; /* Relocation type */
56 WORD offset; /* Offset in segment to fixup */
57 WORD target1; /* Target specification */
58 WORD target2; /* Target specification */
59 };
60
61 /*
62 * Relocation address types
63 */
64 #define NE_RADDR_LOWBYTE 0
65 #define NE_RADDR_SELECTOR 2
66 #define NE_RADDR_POINTER32 3
67 #define NE_RADDR_OFFSET16 5
68 #define NE_RADDR_POINTER48 11
69 #define NE_RADDR_OFFSET32 13
70
71 /*
72 * Relocation types
73 */
74 #define NE_RELTYPE_INTERNAL 0
75 #define NE_RELTYPE_ORDINAL 1
76 #define NE_RELTYPE_NAME 2
77 #define NE_RELTYPE_OSFIXUP 3
78 #define NE_RELFLAG_ADDITIVE 4
79
80 /* Self-loading modules contain this structure in their first segment */
81 typedef struct
82 {
83 WORD version; /* Must be "A0" (0x3041) */
84 WORD reserved;
85 FARPROC16 BootApp; /* startup procedure */
86 FARPROC16 LoadAppSeg; /* procedure to load a segment */
87 FARPROC16 reserved2;
88 FARPROC16 MyAlloc; /* memory allocation procedure,
89 * wine must write this field */
90 FARPROC16 EntryAddrProc;
91 FARPROC16 ExitProc; /* exit procedure */
92 WORD reserved3[4];
93 FARPROC16 SetOwner; /* Set Owner procedure, exported by wine */
94 } SELFLOADHEADER;
95
96 #define SEL(x) ((x)|1)
97
98 static void NE_FixupSegmentPrologs(NE_MODULE *pModule, WORD segnum);
99
100
101 /***********************************************************************
102 * NE_GetRelocAddrName
103 */
104 static const char *NE_GetRelocAddrName( BYTE addr_type, int additive )
105 {
106 switch(addr_type & 0x7f)
107 {
108 case NE_RADDR_LOWBYTE: return additive ? "BYTE add" : "BYTE";
109 case NE_RADDR_OFFSET16: return additive ? "OFFSET16 add" : "OFFSET16";
110 case NE_RADDR_POINTER32: return additive ? "POINTER32 add" : "POINTER32";
111 case NE_RADDR_SELECTOR: return additive ? "SELECTOR add" : "SELECTOR";
112 case NE_RADDR_POINTER48: return additive ? "POINTER48 add" : "POINTER48";
113 case NE_RADDR_OFFSET32: return additive ? "OFFSET32 add" : "OFFSET32";
114 }
115 return "???";
116 }
117
118
119 /***********************************************************************
120 * NE_OpenFile
121 */
122 static HFILE16 NE_OpenFile( NE_MODULE *pModule )
123 {
124 char *name = NE_MODULE_NAME( pModule );
125 HANDLE handle = CreateFileA( name, GENERIC_READ, FILE_SHARE_READ,
126 NULL, OPEN_EXISTING, 0, 0 );
127
128 if (handle == INVALID_HANDLE_VALUE)
129 {
130 ERR( "Can't open file '%s' for module %04x\n", name, pModule->self );
131 return HFILE_ERROR;
132 }
133 return Win32HandleToDosFileHandle( handle );
134 }
135
136
137 /***********************************************************************
138 * apply_relocations
139 *
140 * Apply relocations to a segment. Helper for NE_LoadSegment.
141 */
142 static inline BOOL apply_relocations( NE_MODULE *pModule, const struct relocation_entry_s *rep,
143 int count, int segnum )
144 {
145 BYTE *func_name;
146 char buffer[256];
147 int i, ordinal;
148 WORD offset, *sp;
149 HMODULE16 module;
150 FARPROC16 address = 0;
151 HMODULE16 *pModuleTable = (HMODULE16 *)((char *)pModule + pModule->ne_modtab);
152 SEGTABLEENTRY *pSegTable = NE_SEG_TABLE( pModule );
153 SEGTABLEENTRY *pSeg = pSegTable + segnum - 1;
154
155 /*
156 * Go through the relocation table one entry at a time.
157 */
158 for (i = 0; i < count; i++, rep++)
159 {
160 /*
161 * Get the target address corresponding to this entry.
162 */
163
164 /* If additive, there is no target chain list. Instead, add source
165 and target */
166 int additive = rep->relocation_type & NE_RELFLAG_ADDITIVE;
167 switch (rep->relocation_type & 3)
168 {
169 case NE_RELTYPE_ORDINAL:
170 module = pModuleTable[rep->target1-1];
171 ordinal = rep->target2;
172 address = NE_GetEntryPoint( module, ordinal );
173 if (!address)
174 {
175 NE_MODULE *pTarget = NE_GetPtr( module );
176 if (!pTarget)
177 WARN_(module)("Module not found: %04x, reference %d of module %*.*s\n",
178 module, rep->target1,
179 *((BYTE *)pModule + pModule->ne_restab),
180 *((BYTE *)pModule + pModule->ne_restab),
181 (char *)pModule + pModule->ne_restab + 1 );
182 else
183 {
184 ERR("No implementation for %.*s.%d, setting to 0xdeadbeef\n",
185 *((BYTE *)pTarget + pTarget->ne_restab),
186 (char *)pTarget + pTarget->ne_restab + 1,
187 ordinal );
188 address = (FARPROC16)0xdeadbeef;
189 }
190 }
191 if (TRACE_ON(fixup))
192 {
193 NE_MODULE *pTarget = NE_GetPtr( module );
194 TRACE("%d: %.*s.%d=%04x:%04x %s\n", i + 1,
195 *((BYTE *)pTarget + pTarget->ne_restab),
196 (char *)pTarget + pTarget->ne_restab + 1,
197 ordinal, HIWORD(address), LOWORD(address),
198 NE_GetRelocAddrName( rep->address_type, additive ) );
199 }
200 break;
201
202 case NE_RELTYPE_NAME:
203 module = pModuleTable[rep->target1-1];
204 func_name = (BYTE *)pModule + pModule->ne_imptab + rep->target2;
205 memcpy( buffer, func_name+1, *func_name );
206 buffer[*func_name] = '\0';
207 ordinal = NE_GetOrdinal( module, buffer );
208 address = NE_GetEntryPoint( module, ordinal );
209
210 if (ERR_ON(fixup) && !address)
211 {
212 NE_MODULE *pTarget = NE_GetPtr( module );
213 ERR("No implementation for %.*s.%s, setting to 0xdeadbeef\n",
214 *((BYTE *)pTarget + pTarget->ne_restab),
215 (char *)pTarget + pTarget->ne_restab + 1, buffer );
216 }
217 if (!address) address = (FARPROC16) 0xdeadbeef;
218 if (TRACE_ON(fixup))
219 {
220 NE_MODULE *pTarget = NE_GetPtr( module );
221 TRACE("%d: %.*s.%s=%04x:%04x %s\n", i + 1,
222 *((BYTE *)pTarget + pTarget->ne_restab),
223 (char *)pTarget + pTarget->ne_restab + 1,
224 buffer, HIWORD(address), LOWORD(address),
225 NE_GetRelocAddrName( rep->address_type, additive ) );
226 }
227 break;
228
229 case NE_RELTYPE_INTERNAL:
230 if ((rep->target1 & 0xff) == 0xff)
231 {
232 address = NE_GetEntryPoint( pModule->self, rep->target2 );
233 }
234 else
235 {
236 address = (FARPROC16)MAKESEGPTR( SEL(pSegTable[rep->target1-1].hSeg), rep->target2 );
237 }
238
239 TRACE("%d: %04x:%04x %s\n",
240 i + 1, HIWORD(address), LOWORD(address),
241 NE_GetRelocAddrName( rep->address_type, additive ) );
242 break;
243
244 case NE_RELTYPE_OSFIXUP:
245 /* Relocation type 7:
246 *
247 * These appear to be used as fixups for the Windows
248 * floating point emulator. Let's just ignore them and
249 * try to use the hardware floating point. Linux should
250 * successfully emulate the coprocessor if it doesn't
251 * exist.
252 */
253 TRACE("%d: TYPE %d, OFFSET %04x, TARGET %04x %04x %s\n",
254 i + 1, rep->relocation_type, rep->offset,
255 rep->target1, rep->target2,
256 NE_GetRelocAddrName( rep->address_type, additive ) );
257 continue;
258 }
259
260 offset = rep->offset;
261
262 /* Apparently, high bit of address_type is sometimes set; */
263 /* we ignore it for now */
264 if (rep->address_type > NE_RADDR_OFFSET32)
265 {
266 char module[10];
267 GetModuleName16( pModule->self, module, sizeof(module) );
268 ERR("WARNING: module %s: unknown reloc addr type = 0x%02x. Please report.\n",
269 module, rep->address_type );
270 }
271
272 if (additive)
273 {
274 sp = MapSL( MAKESEGPTR( SEL(pSeg->hSeg), offset ) );
275 TRACE(" %04x:%04x\n", offset, *sp );
276 switch (rep->address_type & 0x7f)
277 {
278 case NE_RADDR_LOWBYTE:
279 *(BYTE *)sp += LOBYTE((int)address);
280 break;
281 case NE_RADDR_OFFSET16:
282 *sp += LOWORD(address);
283 break;
284 case NE_RADDR_POINTER32:
285 *sp += LOWORD(address);
286 *(sp+1) = HIWORD(address);
287 break;
288 case NE_RADDR_SELECTOR:
289 /* Borland creates additive records with offset zero. Strange, but OK */
290 if (*sp)
291 ERR("Additive selector to %04x.Please report\n",*sp);
292 else
293 *sp = HIWORD(address);
294 break;
295 default:
296 goto unknown;
297 }
298 }
299 else /* non-additive fixup */
300 {
301 do
302 {
303 WORD next_offset;
304
305 sp = MapSL( MAKESEGPTR( SEL(pSeg->hSeg), offset ) );
306 next_offset = *sp;
307 TRACE(" %04x:%04x\n", offset, *sp );
308 switch (rep->address_type & 0x7f)
309 {
310 case NE_RADDR_LOWBYTE:
311 *(BYTE *)sp = LOBYTE((int)address);
312 break;
313 case NE_RADDR_OFFSET16:
314 *sp = LOWORD(address);
315 break;
316 case NE_RADDR_POINTER32:
317 *(FARPROC16 *)sp = address;
318 break;
319 case NE_RADDR_SELECTOR:
320 *sp = SELECTOROF(address);
321 break;
322 default:
323 goto unknown;
324 }
325 if (next_offset == offset) break; /* avoid infinite loop */
326 if (next_offset >= GlobalSize16(pSeg->hSeg)) break;
327 offset = next_offset;
328 } while (offset != 0xffff);
329 }
330 }
331 return TRUE;
332
333 unknown:
334 WARN("WARNING: %d: unknown ADDR TYPE %d, "
335 "TYPE %d, OFFSET %04x, TARGET %04x %04x\n",
336 i + 1, rep->address_type, rep->relocation_type,
337 rep->offset, rep->target1, rep->target2);
338 return FALSE;
339 }
340
341
342 /***********************************************************************
343 * NE_LoadSegment
344 */
345 BOOL NE_LoadSegment( NE_MODULE *pModule, WORD segnum )
346 {
347 WORD count;
348 DWORD pos;
349 const struct relocation_entry_s *rep;
350 int size;
351 SEGTABLEENTRY *pSegTable = NE_SEG_TABLE( pModule );
352 SEGTABLEENTRY *pSeg = pSegTable + segnum - 1;
353
354 if (pSeg->flags & NE_SEGFLAGS_LOADED)
355 {
356 /* self-loader ? -> already loaded it */
357 if (pModule->ne_flags & NE_FFLAGS_SELFLOAD)
358 return TRUE;
359
360 /* leave, except for DGROUP, as this may be the second instance */
361 if (segnum != pModule->ne_autodata)
362 return TRUE;
363 }
364
365 if (!pSeg->filepos) return TRUE; /* No file image, just return */
366
367 TRACE_(module)("Loading segment %d, hSeg=%04x, flags=%04x\n",
368 segnum, pSeg->hSeg, pSeg->flags );
369 pos = pSeg->filepos << pModule->ne_align;
370 if (pSeg->size) size = pSeg->size;
371 else size = pSeg->minsize ? pSeg->minsize : 0x10000;
372
373 if (pModule->ne_flags & NE_FFLAGS_SELFLOAD && segnum > 1)
374 {
375 /* Implement self-loading segments */
376 SELFLOADHEADER *selfloadheader;
377 void *oldstack;
378 HFILE16 hFile16;
379 WORD args[3];
380 DWORD ret;
381
382 selfloadheader = MapSL( MAKESEGPTR(SEL(pSegTable->hSeg),0) );
383 oldstack = NtCurrentTeb()->WOW32Reserved;
384 NtCurrentTeb()->WOW32Reserved = (void *)MAKESEGPTR(pModule->self_loading_sel,
385 0xff00 - sizeof(STACK16FRAME));
386
387 hFile16 = NE_OpenFile( pModule );
388 TRACE_(dll)("Call LoadAppSegProc(hmodule=0x%04x,hf=%x,segnum=%d)\n",
389 pModule->self,hFile16,segnum );
390 args[2] = pModule->self;
391 args[1] = hFile16;
392 args[0] = segnum;
393 WOWCallback16Ex( (DWORD)selfloadheader->LoadAppSeg, WCB16_PASCAL, sizeof(args), args, &ret );
394 pSeg->hSeg = LOWORD(ret);
395 TRACE_(dll)("Ret LoadAppSegProc: hSeg=0x%04x\n", pSeg->hSeg);
396 _lclose16( hFile16 );
397 NtCurrentTeb()->WOW32Reserved = oldstack;
398
399 pSeg->flags |= NE_SEGFLAGS_LOADED;
400 return TRUE;
401 }
402 else if (!(pSeg->flags & NE_SEGFLAGS_ITERATED))
403 {
404 void *mem = GlobalLock16(pSeg->hSeg);
405 if (!NE_READ_DATA( pModule, mem, pos, size ))
406 return FALSE;
407 pos += size;
408 }
409 else
410 {
411 /*
412 The following bit of code for "iterated segments" was written without
413 any documentation on the format of these segments. It seems to work,
414 but may be missing something.
415 */
416 const char *buff = NE_GET_DATA( pModule, pos, size );
417 const char* curr = buff;
418 char *mem = GlobalLock16(pSeg->hSeg);
419
420 pos += size;
421 if (buff == NULL) return FALSE;
422
423 while(curr < buff + size) {
424 unsigned int rept = ((const short *)curr)[0];
425 unsigned int len = ((const short *)curr)[1];
426
427 curr += 2*sizeof(short);
428 while (rept--)
429 {
430 memcpy( mem, curr, len );
431 mem += len;
432 }
433 curr += len;
434 }
435 }
436
437 pSeg->flags |= NE_SEGFLAGS_LOADED;
438
439 /* Perform exported function prolog fixups */
440 NE_FixupSegmentPrologs( pModule, segnum );
441
442 if (!(pSeg->flags & NE_SEGFLAGS_RELOC_DATA))
443 return TRUE; /* No relocation data, we are done */
444
445 if (!NE_READ_DATA( pModule, &count, pos, sizeof(count) ) || !count) return TRUE;
446 pos += sizeof(count);
447
448 TRACE("Fixups for %.*s, segment %d, hSeg %04x\n",
449 *((BYTE *)pModule + pModule->ne_restab),
450 (char *)pModule + pModule->ne_restab + 1,
451 segnum, pSeg->hSeg );
452
453 if (!(rep = NE_GET_DATA( pModule, pos, count * sizeof(struct relocation_entry_s) )))
454 return FALSE;
455
456 return apply_relocations( pModule, rep, count, segnum );
457 }
458
459
460 /***********************************************************************
461 * NE_LoadAllSegments
462 */
463 BOOL NE_LoadAllSegments( NE_MODULE *pModule )
464 {
465 int i;
466 SEGTABLEENTRY * pSegTable = NE_SEG_TABLE(pModule);
467
468 if (pModule->ne_flags & NE_FFLAGS_SELFLOAD)
469 {
470 HFILE16 hFile16;
471 HGLOBAL16 sel;
472 /* Handle self-loading modules */
473 SELFLOADHEADER *selfloadheader;
474 HMODULE16 mod = GetModuleHandle16("KERNEL");
475 void *oldstack;
476 WORD args[2];
477
478 TRACE_(module)("%.*s is a self-loading module!\n",
479 *((BYTE*)pModule + pModule->ne_restab),
480 (char *)pModule + pModule->ne_restab + 1);
481 if (!NE_LoadSegment( pModule, 1 )) return FALSE;
482 selfloadheader = MapSL( MAKESEGPTR(SEL(pSegTable->hSeg), 0) );
483 selfloadheader->EntryAddrProc = GetProcAddress16(mod,"EntryAddrProc");
484 selfloadheader->MyAlloc = GetProcAddress16(mod,"MyAlloc");
485 selfloadheader->SetOwner = GetProcAddress16(mod,"FarSetOwner");
486 sel = GlobalAlloc16( GMEM_ZEROINIT, 0xFF00 );
487 pModule->self_loading_sel = SEL(sel);
488 FarSetOwner16( sel, pModule->self );
489 oldstack = NtCurrentTeb()->WOW32Reserved;
490 NtCurrentTeb()->WOW32Reserved = (void *)MAKESEGPTR(pModule->self_loading_sel,
491 0xff00 - sizeof(STACK16FRAME) );
492
493 hFile16 = NE_OpenFile(pModule);
494 TRACE_(dll)("CallBootAppProc(hModule=0x%04x,hf=0x%04x)\n",
495 pModule->self,hFile16);
496 args[1] = pModule->self;
497 args[0] = hFile16;
498 WOWCallback16Ex( (DWORD)selfloadheader->BootApp, WCB16_PASCAL, sizeof(args), args, NULL );
499 TRACE_(dll)("Return from CallBootAppProc\n");
500 _lclose16(hFile16);
501 NtCurrentTeb()->WOW32Reserved = oldstack;
502
503 for (i = 2; i <= pModule->ne_cseg; i++)
504 if (!NE_LoadSegment( pModule, i )) return FALSE;
505 }
506 else
507 {
508 for (i = 1; i <= pModule->ne_cseg; i++)
509 if (!NE_LoadSegment( pModule, i )) return FALSE;
510 }
511 return TRUE;
512 }
513
514
515 /***********************************************************************
516 * NE_FixupSegmentPrologs
517 *
518 * Fixup exported functions prologs of one segment
519 */
520 static void NE_FixupSegmentPrologs(NE_MODULE *pModule, WORD segnum)
521 {
522 SEGTABLEENTRY *pSegTable = NE_SEG_TABLE( pModule );
523 ET_BUNDLE *bundle;
524 ET_ENTRY *entry;
525 WORD dgroup, num_entries, sel = SEL(pSegTable[segnum-1].hSeg);
526 BYTE *pSeg, *pFunc;
527
528 TRACE("(%d);\n", segnum);
529
530 if (pSegTable[segnum-1].flags & NE_SEGFLAGS_DATA)
531 {
532 pSegTable[segnum-1].flags |= NE_SEGFLAGS_LOADED;
533 return;
534 }
535
536 if (!pModule->ne_autodata) return;
537
538 if (!(dgroup = SEL(pSegTable[pModule->ne_autodata-1].hSeg))) return;
539
540 pSeg = MapSL( MAKESEGPTR(sel, 0) );
541
542 bundle = (ET_BUNDLE *)((BYTE *)pModule+pModule->ne_enttab);
543
544 do {
545 TRACE("num_entries: %d, bundle: %p, next: %04x, pSeg: %p\n", bundle->last - bundle->first, bundle, bundle->next, pSeg);
546 if (!(num_entries = bundle->last - bundle->first))
547 return;
548 entry = (ET_ENTRY *)((BYTE *)bundle+6);
549 while (num_entries--)
550 {
551 /*TRACE("entry: %p, entry->segnum: %d, entry->offs: %04x\n", entry, entry->segnum, entry->offs);*/
552 if (entry->segnum == segnum)
553 {
554 pFunc = pSeg+entry->offs;
555 TRACE("pFunc: %p, *(DWORD *)pFunc: %08x, num_entries: %d\n", pFunc, *(DWORD *)pFunc, num_entries);
556 if (*(pFunc+2) == 0x90)
557 {
558 if (*(WORD *)pFunc == 0x581e) /* push ds, pop ax */
559 {
560 TRACE("patch %04x:%04x -> mov ax, ds\n", sel, entry->offs);
561 *(WORD *)pFunc = 0xd88c; /* mov ax, ds */
562 }
563
564 if (*(WORD *)pFunc == 0xd88c)
565 {
566 if ((entry->flags & 2)) /* public data ? */
567 {
568 TRACE("patch %04x:%04x -> mov ax, dgroup [%04x]\n", sel, entry->offs, dgroup);
569 *pFunc = 0xb8; /* mov ax, */
570 *(WORD *)(pFunc+1) = dgroup;
571 }
572 else if ((pModule->ne_flags & NE_FFLAGS_MULTIPLEDATA)
573 && (entry->flags & 1)) /* exported ? */
574 {
575 TRACE("patch %04x:%04x -> nop, nop\n", sel, entry->offs);
576 *(WORD *)pFunc = 0x9090; /* nop, nop */
577 }
578 }
579 }
580 }
581 entry++;
582 }
583 } while ( (bundle->next) && (bundle = ((ET_BUNDLE *)((BYTE *)pModule + bundle->next))) );
584 }
585
586
587 /***********************************************************************
588 * PatchCodeHandle (KERNEL.110)
589 *
590 * Needed for self-loading modules.
591 */
592 DWORD WINAPI PatchCodeHandle16(HANDLE16 hSeg)
593 {
594 WORD segnum;
595 WORD sel = SEL(hSeg);
596 NE_MODULE *pModule = NE_GetPtr(FarGetOwner16(sel));
597 SEGTABLEENTRY *pSegTable = NE_SEG_TABLE(pModule);
598
599 TRACE_(module)("(%04x);\n", hSeg);
600
601 /* find the segment number of the module that belongs to hSeg */
602 for (segnum = 1; segnum <= pModule->ne_cseg; segnum++)
603 {
604 if (SEL(pSegTable[segnum-1].hSeg) == sel)
605 {
606 NE_FixupSegmentPrologs(pModule, segnum);
607 break;
608 }
609 }
610
611 return MAKELONG(hSeg, sel);
612 }
613
614
615 /***********************************************************************
616 * NE_GetDLLInitParams
617 */
618 static VOID NE_GetDLLInitParams( NE_MODULE *pModule,
619 WORD *hInst, WORD *ds, WORD *heap )
620 {
621 SEGTABLEENTRY *pSegTable = NE_SEG_TABLE( pModule );
622
623 if (!(pModule->ne_flags & NE_FFLAGS_SINGLEDATA))
624 {
625 if (pModule->ne_flags & NE_FFLAGS_MULTIPLEDATA || pModule->ne_autodata)
626 {
627 /* Not SINGLEDATA */
628 ERR_(dll)("Library is not marked SINGLEDATA\n");
629 exit(1);
630 }
631 else /* DATA NONE DLL */
632 {
633 *ds = 0;
634 *heap = 0;
635 }
636 }
637 else /* DATA SINGLE DLL */
638 {
639 if (pModule->ne_autodata) {
640 *ds = SEL(pSegTable[pModule->ne_autodata-1].hSeg);
641 *heap = pModule->ne_heap;
642 }
643 else /* hmm, DLL has no dgroup,
644 but why has it NE_FFLAGS_SINGLEDATA set ?
645 Buggy DLL compiler ? */
646 {
647 *ds = 0;
648 *heap = 0;
649 }
650 }
651
652 *hInst = *ds ? GlobalHandle16(*ds) : pModule->self;
653 }
654
655
656 /***********************************************************************
657 * NE_InitDLL
658 *
659 * Call the DLL initialization code
660 */
661 static BOOL NE_InitDLL( NE_MODULE *pModule )
662 {
663 SEGTABLEENTRY *pSegTable;
664 WORD hInst, ds, heap;
665 CONTEXT86 context;
666
667 pSegTable = NE_SEG_TABLE( pModule );
668
669 if (!(pModule->ne_flags & NE_FFLAGS_LIBMODULE) ||
670 (pModule->ne_flags & NE_FFLAGS_WIN32)) return TRUE; /*not a library*/
671
672 /* Call USER signal handler for Win3.1 compatibility. */
673 NE_CallUserSignalProc( pModule->self, USIG16_DLL_LOAD );
674
675 if (!SELECTOROF(pModule->ne_csip)) return TRUE; /* no initialization code */
676
677
678 /* Registers at initialization must be:
679 * cx heap size
680 * di library instance
681 * ds data segment if any
682 * es:si command line (always 0)
683 */
684
685 memset( &context, 0, sizeof(context) );
686
687 NE_GetDLLInitParams( pModule, &hInst, &ds, &heap );
688
689 context.Ecx = heap;
690 context.Edi = hInst;
691 context.SegDs = ds;
692 context.SegEs = ds; /* who knows ... */
693 context.SegFs = wine_get_fs();
694 context.SegGs = wine_get_gs();
695 context.SegCs = SEL(pSegTable[SELECTOROF(pModule->ne_csip)-1].hSeg);
696 context.Eip = OFFSETOF(pModule->ne_csip);
697 context.Ebp = OFFSETOF(NtCurrentTeb()->WOW32Reserved) + FIELD_OFFSET(STACK16FRAME,bp);
698
699 pModule->ne_csip = 0; /* Don't initialize it twice */
700 TRACE_(dll)("Calling LibMain for %.*s, cs:ip=%04x:%04x ds=%04x di=%04x cx=%04x\n",
701 *((BYTE*)pModule + pModule->ne_restab),
702 (char *)pModule + pModule->ne_restab + 1,
703 context.SegCs, context.Eip, context.SegDs,
704 LOWORD(context.Edi), LOWORD(context.Ecx) );
705 WOWCallback16Ex( 0, WCB16_REGS, 0, NULL, (DWORD *)&context );
706 return TRUE;
707 }
708
709 /***********************************************************************
710 * NE_InitializeDLLs
711 *
712 * Recursively initialize all DLLs (according to the order in which
713 * they where loaded).
714 */
715 void NE_InitializeDLLs( HMODULE16 hModule )
716 {
717 NE_MODULE *pModule;
718 HMODULE16 *pDLL;
719
720 if (!(pModule = NE_GetPtr( hModule ))) return;
721 assert( !(pModule->ne_flags & NE_FFLAGS_WIN32) );
722
723 if (pModule->dlls_to_init)
724 {
725 HGLOBAL16 to_init = pModule->dlls_to_init;
726 pModule->dlls_to_init = 0;
727 for (pDLL = (HMODULE16 *)GlobalLock16( to_init ); *pDLL; pDLL++)
728 {
729 NE_InitializeDLLs( *pDLL );
730 }
731 GlobalFree16( to_init );
732 }
733 NE_InitDLL( pModule );
734 }
735
736
737 /**********************************************************************
738 * NE_CallUserSignalProc
739 *
740 * According to "Undocumented Windows", the task signal proc is
741 * bypassed for module load/unload notifications, and the USER signal
742 * proc is called directly instead. This is what this function does.
743 */
744 typedef DWORD (WINAPI *pSignalProc)( HANDLE16 module, UINT16 code, UINT16 exit,
745 HINSTANCE16 inst, HQUEUE16 queue );
746
747 void NE_CallUserSignalProc( HMODULE16 hModule, UINT16 code )
748 {
749 FARPROC16 proc;
750 HMODULE16 user = GetModuleHandle16("user.exe");
751
752 if (!user) return;
753 if ((proc = GetProcAddress16( user, "SignalProc" )))
754 {
755 /* USER is always a builtin dll */
756 pSignalProc sigproc = (pSignalProc)((ENTRYPOINT16 *)MapSL( (SEGPTR)proc ))->target;
757 sigproc( hModule, code, 0, 0, 0 );
758 }
759 }
760
761
762 /***********************************************************************
763 * NE_CallDllEntryPoint
764 *
765 * Call the DllEntryPoint of DLLs with subsystem >= 4.0
766 */
767 typedef DWORD (WINAPI *WinNEEntryProc)(DWORD,WORD,WORD,WORD,DWORD,WORD);
768
769 static void NE_CallDllEntryPoint( NE_MODULE *pModule, DWORD dwReason )
770 {
771 WORD hInst, ds, heap;
772 FARPROC16 entryPoint;
773
774 if (!(pModule->ne_flags & NE_FFLAGS_LIBMODULE)) return;
775 if (!(pModule->ne_flags & NE_FFLAGS_BUILTIN) && pModule->ne_expver < 0x0400) return;
776 if (!(entryPoint = GetProcAddress16( pModule->self, "DllEntryPoint" ))) return;
777
778 NE_GetDLLInitParams( pModule, &hInst, &ds, &heap );
779
780 TRACE_(dll)( "Calling %s DllEntryPoint, cs:ip=%04x:%04x\n",
781 NE_MODULE_NAME( pModule ),
782 SELECTOROF(entryPoint), OFFSETOF(entryPoint) );
783
784 if ( pModule->ne_flags & NE_FFLAGS_BUILTIN )
785 {
786 WinNEEntryProc entryProc = (WinNEEntryProc)((ENTRYPOINT16 *)MapSL( (SEGPTR)entryPoint ))->target;
787
788 entryProc( dwReason, hInst, ds, heap, 0, 0 );
789 }
790 else
791 {
792 CONTEXT86 context;
793 WORD args[8];
794
795 memset( &context, 0, sizeof(context) );
796 context.SegDs = ds;
797 context.SegEs = ds; /* who knows ... */
798 context.SegFs = wine_get_fs();
799 context.SegGs = wine_get_gs();
800 context.SegCs = HIWORD(entryPoint);
801 context.Eip = LOWORD(entryPoint);
802 context.Ebp = OFFSETOF(NtCurrentTeb()->WOW32Reserved) + FIELD_OFFSET(STACK16FRAME,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->ne_flags & NE_FFLAGS_WIN32) );
888
889 /* Never add a module twice */
890 for ( i = 0; i < list->count; i++ )
891 if ( <