Move the FPS computation from the D3D code to the common code.
[wine/multimedia.git] / dlls / kernel / ne_segment.c
blobfe02b46a00d03014fc6ed5fc588316eda85fcec6
1 /*
2 * NE segment loading
4 * Copyright 1993 Robert J. Amstadt
5 * Copyright 1995 Alexandre Julliard
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.
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.
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
22 #include "config.h"
23 #include "wine/port.h"
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>
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"
46 WINE_DEFAULT_DEBUG_CHANNEL(fixup);
47 WINE_DECLARE_DEBUG_CHANNEL(dll);
48 WINE_DECLARE_DEBUG_CHANNEL(module);
51 * Relocation table entry
53 struct relocation_entry_s
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 */
63 * Relocation address types
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
73 * Relocation types
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
81 /* Self-loading modules contain this structure in their first segment */
82 typedef struct
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;
97 #define SEL(x) ((x)|1)
99 static void NE_FixupSegmentPrologs(NE_MODULE *pModule, WORD segnum);
102 /***********************************************************************
103 * NE_GetRelocAddrName
105 static const char *NE_GetRelocAddrName( BYTE addr_type, int additive )
107 switch(addr_type & 0x7f)
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";
116 return "???";
120 /***********************************************************************
121 * NE_LoadSegment
123 BOOL NE_LoadSegment( NE_MODULE *pModule, WORD segnum )
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;
137 char buffer[256];
138 int ordinal, additive;
139 unsigned short *sp;
141 pSegTable = NE_SEG_TABLE( pModule );
142 pSeg = pSegTable + segnum - 1;
144 if (pSeg->flags & NE_SEGFLAGS_LOADED)
146 /* self-loader ? -> already loaded it */
147 if (pModule->flags & NE_FFLAGS_SELFLOAD)
148 return TRUE;
150 /* leave, except for DGROUP, as this may be the second instance */
151 if (segnum != pModule->dgroup)
152 return TRUE;
155 if (!pSeg->filepos) return TRUE; /* No file image, just return */
157 pModuleTable = (HMODULE16 *)((char *)pModule + pModule->modref_table);
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)
168 /* Implement self-loading segments */
169 SELFLOADHEADER *selfloadheader;
170 DWORD oldstack;
171 HANDLE hFile32;
172 HFILE16 hFile16;
173 WORD args[3];
174 DWORD ret;
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));
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;
195 else if (!(pSeg->flags & NE_SEGFLAGS_ITERATED))
196 ReadFile(hf, mem, size, &res, NULL);
197 else {
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
204 char* buff = HeapAlloc(GetProcessHeap(), 0, size);
205 char* curr = buff;
207 if(buff == NULL) {
208 WARN_(dll)("Memory exhausted!\n");
209 goto fail;
212 ReadFile(hf, buff, size, &res, NULL);
213 while(curr < buff + size) {
214 unsigned int rept = *((short*) curr)++;
215 unsigned int len = *((short*) curr)++;
216 for(; rept > 0; rept--) {
217 char* bytes = curr;
218 unsigned int byte;
219 for(byte = 0; byte < len; byte++)
220 *mem++ = *bytes++;
222 curr += len;
224 HeapFree(GetProcessHeap(), 0, buff);
227 pSeg->flags |= NE_SEGFLAGS_LOADED;
229 /* Perform exported function prolog fixups */
230 NE_FixupSegmentPrologs( pModule, segnum );
232 if (!(pSeg->flags & NE_SEGFLAGS_RELOC_DATA))
233 goto succeed; /* No relocation data, we are done */
235 ReadFile(hf, &count, sizeof(count), &res, NULL);
236 if (!count) goto succeed;
238 TRACE("Fixups for %.*s, segment %d, hSeg %04x\n",
239 *((BYTE *)pModule + pModule->name_table),
240 (char *)pModule + pModule->name_table + 1,
241 segnum, pSeg->hSeg );
243 reloc_entries = (struct relocation_entry_s *)HeapAlloc(GetProcessHeap(), 0, count * sizeof(struct relocation_entry_s));
244 if(reloc_entries == NULL) {
245 WARN("Not enough memory for relocation entries!\n");
246 goto fail;
248 if (!ReadFile( hf, reloc_entries, count * sizeof(struct relocation_entry_s), &res, NULL) ||
249 (res != count * sizeof(struct relocation_entry_s)))
251 WARN("Unable to read relocation information\n" );
252 goto fail;
256 * Go through the relocation table one entry at a time.
258 rep = reloc_entries;
259 for (i = 0; i < count; i++, rep++)
262 * Get the target address corresponding to this entry.
265 /* If additive, there is no target chain list. Instead, add source
266 and target */
267 additive = rep->relocation_type & NE_RELFLAG_ADDITIVE;
268 rep->relocation_type &= 0x3;
270 switch (rep->relocation_type)
272 case NE_RELTYPE_ORDINAL:
273 module = pModuleTable[rep->target1-1];
274 ordinal = rep->target2;
275 address = NE_GetEntryPoint( module, ordinal );
276 if (!address)
278 NE_MODULE *pTarget = NE_GetPtr( module );
279 if (!pTarget)
280 WARN_(module)("Module not found: %04x, reference %d of module %*.*s\n",
281 module, rep->target1,
282 *((BYTE *)pModule + pModule->name_table),
283 *((BYTE *)pModule + pModule->name_table),
284 (char *)pModule + pModule->name_table + 1 );
285 else
287 ERR("No implementation for %.*s.%d, setting to 0xdeadbeef\n",
288 *((BYTE *)pTarget + pTarget->name_table),
289 (char *)pTarget + pTarget->name_table + 1,
290 ordinal );
291 address = (FARPROC16)0xdeadbeef;
294 if (TRACE_ON(fixup))
296 NE_MODULE *pTarget = NE_GetPtr( module );
297 TRACE("%d: %.*s.%d=%04x:%04x %s\n", i + 1,
298 *((BYTE *)pTarget + pTarget->name_table),
299 (char *)pTarget + pTarget->name_table + 1,
300 ordinal, HIWORD(address), LOWORD(address),
301 NE_GetRelocAddrName( rep->address_type, additive ) );
303 break;
305 case NE_RELTYPE_NAME:
306 module = pModuleTable[rep->target1-1];
307 func_name = (char *)pModule + pModule->import_table + rep->target2;
308 memcpy( buffer, func_name+1, *func_name );
309 buffer[*func_name] = '\0';
310 func_name = buffer;
311 ordinal = NE_GetOrdinal( module, func_name );
312 address = NE_GetEntryPoint( module, ordinal );
314 if (ERR_ON(fixup) && !address)
316 NE_MODULE *pTarget = NE_GetPtr( module );
317 ERR("No implementation for %.*s.%s, setting to 0xdeadbeef\n",
318 *((BYTE *)pTarget + pTarget->name_table),
319 (char *)pTarget + pTarget->name_table + 1, func_name );
321 if (!address) address = (FARPROC16) 0xdeadbeef;
322 if (TRACE_ON(fixup))
324 NE_MODULE *pTarget = NE_GetPtr( module );
325 TRACE("%d: %.*s.%s=%04x:%04x %s\n", i + 1,
326 *((BYTE *)pTarget + pTarget->name_table),
327 (char *)pTarget + pTarget->name_table + 1,
328 func_name, HIWORD(address), LOWORD(address),
329 NE_GetRelocAddrName( rep->address_type, additive ) );
331 break;
333 case NE_RELTYPE_INTERNAL:
334 if ((rep->target1 & 0xff) == 0xff)
336 address = NE_GetEntryPoint( pModule->self, rep->target2 );
338 else
340 address = (FARPROC16)MAKESEGPTR( SEL(pSegTable[rep->target1-1].hSeg), rep->target2 );
343 TRACE("%d: %04x:%04x %s\n",
344 i + 1, HIWORD(address), LOWORD(address),
345 NE_GetRelocAddrName( rep->address_type, additive ) );
346 break;
348 case NE_RELTYPE_OSFIXUP:
349 /* Relocation type 7:
351 * These appear to be used as fixups for the Windows
352 * floating point emulator. Let's just ignore them and
353 * try to use the hardware floating point. Linux should
354 * successfully emulate the coprocessor if it doesn't
355 * exist.
357 TRACE("%d: TYPE %d, OFFSET %04x, TARGET %04x %04x %s\n",
358 i + 1, rep->relocation_type, rep->offset,
359 rep->target1, rep->target2,
360 NE_GetRelocAddrName( rep->address_type, additive ) );
361 continue;
364 offset = rep->offset;
366 /* Apparently, high bit of address_type is sometimes set; */
367 /* we ignore it for now */
368 if (rep->address_type > NE_RADDR_OFFSET32)
370 char module[10];
371 GetModuleName16( pModule->self, module, sizeof(module) );
372 ERR("WARNING: module %s: unknown reloc addr type = 0x%02x. Please report.\n",
373 module, rep->address_type );
376 if (additive)
378 sp = MapSL( MAKESEGPTR( SEL(pSeg->hSeg), offset ) );
379 TRACE(" %04x:%04x\n", offset, *sp );
380 switch (rep->address_type & 0x7f)
382 case NE_RADDR_LOWBYTE:
383 *(BYTE *)sp += LOBYTE((int)address);
384 break;
385 case NE_RADDR_OFFSET16:
386 *sp += LOWORD(address);
387 break;
388 case NE_RADDR_POINTER32:
389 *sp += LOWORD(address);
390 *(sp+1) = HIWORD(address);
391 break;
392 case NE_RADDR_SELECTOR:
393 /* Borland creates additive records with offset zero. Strange, but OK */
394 if (*sp)
395 ERR("Additive selector to %04x.Please report\n",*sp);
396 else
397 *sp = HIWORD(address);
398 break;
399 default:
400 goto unknown;
403 else /* non-additive fixup */
407 sp = MapSL( MAKESEGPTR( SEL(pSeg->hSeg), offset ) );
408 next_offset = *sp;
409 TRACE(" %04x:%04x\n", offset, *sp );
410 switch (rep->address_type & 0x7f)
412 case NE_RADDR_LOWBYTE:
413 *(BYTE *)sp = LOBYTE((int)address);
414 break;
415 case NE_RADDR_OFFSET16:
416 *sp = LOWORD(address);
417 break;
418 case NE_RADDR_POINTER32:
419 *(FARPROC16 *)sp = address;
420 break;
421 case NE_RADDR_SELECTOR:
422 *sp = SELECTOROF(address);
423 break;
424 default:
425 goto unknown;
427 if (next_offset == offset) break; /* avoid infinite loop */
428 if (next_offset >= GlobalSize16(pSeg->hSeg)) break;
429 offset = next_offset;
430 } while (offset != 0xffff);
434 HeapFree(GetProcessHeap(), 0, reloc_entries);
436 succeed:
437 CloseHandle(hf);
438 return TRUE;
440 unknown:
441 WARN("WARNING: %d: unknown ADDR TYPE %d, "
442 "TYPE %d, OFFSET %04x, TARGET %04x %04x\n",
443 i + 1, rep->address_type, rep->relocation_type,
444 rep->offset, rep->target1, rep->target2);
445 HeapFree(GetProcessHeap(), 0, reloc_entries);
447 fail:
448 CloseHandle(hf);
449 return FALSE;
453 /***********************************************************************
454 * NE_LoadAllSegments
456 BOOL NE_LoadAllSegments( NE_MODULE *pModule )
458 int i;
459 SEGTABLEENTRY * pSegTable = (SEGTABLEENTRY *) NE_SEG_TABLE(pModule);
461 if (pModule->flags & NE_FFLAGS_SELFLOAD)
463 HANDLE hf;
464 HFILE16 hFile16;
465 HGLOBAL16 sel;
466 /* Handle self-loading modules */
467 SELFLOADHEADER *selfloadheader;
468 HMODULE16 mod = GetModuleHandle16("KERNEL");
469 DWORD oldstack;
470 WORD args[2];
472 TRACE_(module)("%.*s is a self-loading module!\n",
473 *((BYTE*)pModule + pModule->name_table),
474 (char *)pModule + pModule->name_table + 1);
475 if (!NE_LoadSegment( pModule, 1 )) return FALSE;
476 selfloadheader = MapSL( MAKESEGPTR(SEL(pSegTable->hSeg), 0) );
477 selfloadheader->EntryAddrProc = GetProcAddress16(mod,"EntryAddrProc");
478 selfloadheader->MyAlloc = GetProcAddress16(mod,"MyAlloc");
479 selfloadheader->SetOwner = GetProcAddress16(mod,"FarSetOwner");
480 sel = GlobalAlloc16( GMEM_ZEROINIT, 0xFF00 );
481 pModule->self_loading_sel = SEL(sel);
482 FarSetOwner16( sel, pModule->self );
483 oldstack = NtCurrentTeb()->cur_stack;
484 NtCurrentTeb()->cur_stack = MAKESEGPTR(pModule->self_loading_sel,
485 0xff00 - sizeof(STACK16FRAME) );
487 hf = NE_OpenFile(pModule);
488 hFile16 = Win32HandleToDosFileHandle( hf );
489 TRACE_(dll)("CallBootAppProc(hModule=0x%04x,hf=0x%04x)\n",
490 pModule->self,hFile16);
491 args[1] = pModule->self;
492 args[0] = hFile16;
493 WOWCallback16Ex( (DWORD)selfloadheader->BootApp, WCB16_PASCAL, sizeof(args), args, NULL );
494 TRACE_(dll)("Return from CallBootAppProc\n");
495 _lclose16(hFile16);
496 NtCurrentTeb()->cur_stack = oldstack;
498 for (i = 2; i <= pModule->seg_count; i++)
499 if (!NE_LoadSegment( pModule, i )) return FALSE;
501 else
503 for (i = 1; i <= pModule->seg_count; i++)
504 if (!NE_LoadSegment( pModule, i )) return FALSE;
506 return TRUE;
510 /***********************************************************************
511 * NE_FixupSegmentPrologs
513 * Fixup exported functions prologs of one segment
515 static void NE_FixupSegmentPrologs(NE_MODULE *pModule, WORD segnum)
517 SEGTABLEENTRY *pSegTable = NE_SEG_TABLE( pModule );
518 ET_BUNDLE *bundle;
519 ET_ENTRY *entry;
520 WORD dgroup, num_entries, sel = SEL(pSegTable[segnum-1].hSeg);
521 BYTE *pSeg, *pFunc;
523 TRACE("(%d);\n", segnum);
525 if (pSegTable[segnum-1].flags & NE_SEGFLAGS_DATA)
527 pSegTable[segnum-1].flags |= NE_SEGFLAGS_LOADED;
528 return;
531 if (!pModule->dgroup) return;
533 if (!(dgroup = SEL(pSegTable[pModule->dgroup-1].hSeg))) return;
535 pSeg = MapSL( MAKESEGPTR(sel, 0) );
537 bundle = (ET_BUNDLE *)((BYTE *)pModule+pModule->entry_table);
539 do {
540 TRACE("num_entries: %d, bundle: %p, next: %04x, pSeg: %p\n", bundle->last - bundle->first, bundle, bundle->next, pSeg);
541 if (!(num_entries = bundle->last - bundle->first))
542 return;
543 entry = (ET_ENTRY *)((BYTE *)bundle+6);
544 while (num_entries--)
546 /*TRACE("entry: %p, entry->segnum: %d, entry->offs: %04x\n", entry, entry->segnum, entry->offs);*/
547 if (entry->segnum == segnum)
549 pFunc = ((BYTE *)pSeg+entry->offs);
550 TRACE("pFunc: %p, *(DWORD *)pFunc: %08lx, num_entries: %d\n", pFunc, *(DWORD *)pFunc, num_entries);
551 if (*(pFunc+2) == 0x90)
553 if (*(WORD *)pFunc == 0x581e) /* push ds, pop ax */
555 TRACE("patch %04x:%04x -> mov ax, ds\n", sel, entry->offs);
556 *(WORD *)pFunc = 0xd88c; /* mov ax, ds */
559 if (*(WORD *)pFunc == 0xd88c)
561 if ((entry->flags & 2)) /* public data ? */
563 TRACE("patch %04x:%04x -> mov ax, dgroup [%04x]\n", sel, entry->offs, dgroup);
564 *pFunc = 0xb8; /* mov ax, */
565 *(WORD *)(pFunc+1) = dgroup;
567 else
568 if ((pModule->flags & NE_FFLAGS_MULTIPLEDATA)
569 && (entry->flags & 1)) /* exported ? */
571 TRACE("patch %04x:%04x -> nop, nop\n", sel, entry->offs);
572 *(WORD *)pFunc = 0x9090; /* nop, nop */
577 entry++;
579 } while ( (bundle->next)
580 && (bundle = ((ET_BUNDLE *)((BYTE *)pModule + bundle->next))) );
584 /***********************************************************************
585 * PatchCodeHandle (KERNEL.110)
587 * Needed for self-loading modules.
589 DWORD WINAPI PatchCodeHandle16(HANDLE16 hSeg)
591 WORD segnum;
592 WORD sel = SEL(hSeg);
593 NE_MODULE *pModule = NE_GetPtr(FarGetOwner16(sel));
594 SEGTABLEENTRY *pSegTable = NE_SEG_TABLE(pModule);
596 TRACE_(module)("(%04x);\n", hSeg);
598 /* find the segment number of the module that belongs to hSeg */
599 for (segnum = 1; segnum <= pModule->seg_count; segnum++)
601 if (SEL(pSegTable[segnum-1].hSeg) == sel)
603 NE_FixupSegmentPrologs(pModule, segnum);
604 break;
608 return MAKELONG(hSeg, sel);
612 /***********************************************************************
613 * NE_GetDLLInitParams
615 static VOID NE_GetDLLInitParams( NE_MODULE *pModule,
616 WORD *hInst, WORD *ds, WORD *heap )
618 SEGTABLEENTRY *pSegTable = NE_SEG_TABLE( pModule );
620 if (!(pModule->flags & NE_FFLAGS_SINGLEDATA))
622 if (pModule->flags & NE_FFLAGS_MULTIPLEDATA || pModule->dgroup)
624 /* Not SINGLEDATA */
625 ERR_(dll)("Library is not marked SINGLEDATA\n");
626 exit(1);
628 else /* DATA NONE DLL */
630 *ds = 0;
631 *heap = 0;
634 else /* DATA SINGLE DLL */
636 if (pModule->dgroup) {
637 *ds = SEL(pSegTable[pModule->dgroup-1].hSeg);
638 *heap = pModule->heap_size;
640 else /* hmm, DLL has no dgroup,
641 but why has it NE_FFLAGS_SINGLEDATA set ?
642 Buggy DLL compiler ? */
644 *ds = 0;
645 *heap = 0;
649 *hInst = *ds ? GlobalHandle16(*ds) : pModule->self;
653 /***********************************************************************
654 * NE_InitDLL
656 * Call the DLL initialization code
658 static BOOL NE_InitDLL( NE_MODULE *pModule )
660 SEGTABLEENTRY *pSegTable;
661 WORD hInst, ds, heap;
662 CONTEXT86 context;
664 pSegTable = NE_SEG_TABLE( pModule );
666 if (!(pModule->flags & NE_FFLAGS_LIBMODULE) ||
667 (pModule->flags & NE_FFLAGS_WIN32)) return TRUE; /*not a library*/
669 /* Call USER signal handler for Win3.1 compatibility. */
670 NE_CallUserSignalProc( pModule->self, USIG16_DLL_LOAD );
672 if (!pModule->cs) return TRUE; /* no initialization code */
675 /* Registers at initialization must be:
676 * cx heap size
677 * di library instance
678 * ds data segment if any
679 * es:si command line (always 0)
682 memset( &context, 0, sizeof(context) );
684 NE_GetDLLInitParams( pModule, &hInst, &ds, &heap );
686 context.Ecx = heap;
687 context.Edi = hInst;
688 context.SegDs = ds;
689 context.SegEs = ds; /* who knows ... */
690 context.SegFs = wine_get_fs();
691 context.SegGs = wine_get_gs();
692 context.SegCs = SEL(pSegTable[pModule->cs-1].hSeg);
693 context.Eip = pModule->ip;
694 context.Ebp = OFFSETOF(NtCurrentTeb()->cur_stack) + (WORD)&((STACK16FRAME*)0)->bp;
696 pModule->cs = 0; /* Don't initialize it twice */
697 TRACE_(dll)("Calling LibMain for %.*s, cs:ip=%04lx:%04lx ds=%04lx di=%04x cx=%04x\n",
698 *((BYTE*)pModule + pModule->name_table),
699 (char *)pModule + pModule->name_table + 1,
700 context.SegCs, context.Eip, context.SegDs,
701 LOWORD(context.Edi), LOWORD(context.Ecx) );
702 WOWCallback16Ex( 0, WCB16_REGS, 0, NULL, (DWORD *)&context );
703 return TRUE;
706 /***********************************************************************
707 * NE_InitializeDLLs
709 * Recursively initialize all DLLs (according to the order in which
710 * they where loaded).
712 void NE_InitializeDLLs( HMODULE16 hModule )
714 NE_MODULE *pModule;
715 HMODULE16 *pDLL;
717 if (!(pModule = NE_GetPtr( hModule ))) return;
718 assert( !(pModule->flags & NE_FFLAGS_WIN32) );
720 if (pModule->dlls_to_init)
722 HGLOBAL16 to_init = pModule->dlls_to_init;
723 pModule->dlls_to_init = 0;
724 for (pDLL = (HMODULE16 *)GlobalLock16( to_init ); *pDLL; pDLL++)
726 NE_InitializeDLLs( *pDLL );
728 GlobalFree16( to_init );
730 NE_InitDLL( pModule );
734 /**********************************************************************
735 * NE_CallUserSignalProc
737 * According to "Undocumented Windows", the task signal proc is
738 * bypassed for module load/unload notifications, and the USER signal
739 * proc is called directly instead. This is what this function does.
741 typedef DWORD (WINAPI *pSignalProc)( HANDLE16 module, UINT16 code, UINT16 exit,
742 HINSTANCE16 inst, HQUEUE16 queue );
744 void NE_CallUserSignalProc( HMODULE16 hModule, UINT16 code )
746 FARPROC16 proc;
747 HMODULE16 user = GetModuleHandle16("user.exe");
749 if (!user) return;
750 if ((proc = GetProcAddress16( user, "SignalProc" )))
752 /* USER is always a builtin dll */
753 pSignalProc sigproc = (pSignalProc)((ENTRYPOINT16 *)MapSL( (SEGPTR)proc ))->target;
754 sigproc( hModule, code, 0, 0, 0 );
759 /***********************************************************************
760 * NE_CallDllEntryPoint
762 * Call the DllEntryPoint of DLLs with subsystem >= 4.0
764 typedef DWORD (WINAPI *WinNEEntryProc)(DWORD,WORD,WORD,WORD,DWORD,WORD);
766 static void NE_CallDllEntryPoint( NE_MODULE *pModule, DWORD dwReason )
768 WORD hInst, ds, heap;
769 FARPROC16 entryPoint;
771 if (!(pModule->flags & NE_FFLAGS_LIBMODULE)) return;
772 if (!(pModule->flags & NE_FFLAGS_BUILTIN) && pModule->expected_version < 0x0400) return;
773 if (!(entryPoint = GetProcAddress16( pModule->self, "DllEntryPoint" ))) return;
775 NE_GetDLLInitParams( pModule, &hInst, &ds, &heap );
777 TRACE_(dll)( "Calling %s DllEntryPoint, cs:ip=%04x:%04x\n",
778 NE_MODULE_NAME( pModule ),
779 SELECTOROF(entryPoint), OFFSETOF(entryPoint) );
781 if ( pModule->flags & NE_FFLAGS_BUILTIN )
783 WinNEEntryProc entryProc = (WinNEEntryProc)((ENTRYPOINT16 *)MapSL( (SEGPTR)entryPoint ))->target;
785 entryProc( dwReason, hInst, ds, heap, 0, 0 );
787 else
789 CONTEXT86 context;
790 WORD args[8];
792 memset( &context, 0, sizeof(context) );
793 context.SegDs = ds;
794 context.SegEs = ds; /* who knows ... */
795 context.SegFs = wine_get_fs();
796 context.SegGs = wine_get_gs();
797 context.SegCs = HIWORD(entryPoint);
798 context.Eip = LOWORD(entryPoint);
799 context.Ebp = OFFSETOF( NtCurrentTeb()->cur_stack )
800 + (WORD)&((STACK16FRAME*)0)->bp;
802 args[7] = HIWORD(dwReason);
803 args[6] = LOWORD(dwReason);
804 args[5] = hInst;
805 args[4] = ds;
806 args[3] = heap;
807 args[2] = 0; /* HIWORD(dwReserved1) */
808 args[1] = 0; /* LOWORD(dwReserved1) */
809 args[0] = 0; /* wReserved2 */
810 WOWCallback16Ex( 0, WCB16_REGS, sizeof(args), args, (DWORD *)&context );
814 /***********************************************************************
815 * NE_DllProcessAttach
817 * Call the DllEntryPoint of all modules this one (recursively)
818 * depends on, according to the order in which they were loaded.
820 * Note that --as opposed to the PE module case-- there is no notion
821 * of 'module loaded into a process' for NE modules, and hence we
822 * have no place to store the fact that the DllEntryPoint of a
823 * given module was already called on behalf of this process (e.g.
824 * due to some earlier LoadLibrary16 call).
826 * Thus, we just call the DllEntryPoint twice in that case. Win9x
827 * appears to behave this way as well ...
829 * This routine must only be called with the Win16Lock held.
831 * FIXME: We should actually abort loading in case the DllEntryPoint
832 * returns FALSE ...
836 struct ne_init_list
838 int count;
839 int size;
840 NE_MODULE **module;
843 static void add_to_init_list( struct ne_init_list *list, NE_MODULE *hModule )
845 NE_MODULE **newModule = NULL;
846 if ( list->count == list->size )
848 int newSize = list->size + 128;
850 if (list->module)
851 newModule = HeapReAlloc( GetProcessHeap(), 0,
852 list->module, newSize*sizeof(NE_MODULE *) );
853 else
854 newModule = HeapAlloc( GetProcessHeap(), 0,
855 newSize*sizeof(NE_MODULE *) );
856 if ( !newModule )
858 FIXME_(dll)("Out of memory!\n");
859 return;
862 list->module = newModule;
863 list->size = newSize;
866 list->module[list->count++] = hModule;
869 static void free_init_list( struct ne_init_list *list )
871 if ( list->module )
873 HeapFree( GetProcessHeap(), 0, list->module );
874 memset( list, 0, sizeof(*list) );
878 static void fill_init_list( struct ne_init_list *list, HMODULE16 hModule )
880 NE_MODULE *pModule;
881 HMODULE16 *pModRef;
882 int i;
884 if (!(pModule = NE_GetPtr( hModule ))) return;
885 assert( !(pModule->flags & NE_FFLAGS_WIN32) );
887 /* Never add a module twice */
888 for ( i = 0; i < list->count; i++ )
889 if ( list->module[i] == pModule )
890 return;
892 /* Check for recursive call */
893 if ( pModule->misc_flags & 0x80 ) return;
895 TRACE_(dll)("(%s) - START\n", NE_MODULE_NAME(pModule) );
897 /* Tag current module to prevent recursive loop */
898 pModule->misc_flags |= 0x80;
900 /* Recursively attach all DLLs this one depends on */
901 pModRef = (HMODULE16 *)((char *)pModule + pModule->modref_table);
902 for ( i = 0; i < pModule->modref_count; i++ )
903 if ( pModRef[i] ) fill_init_list( list, pModRef[i] );
905 /* Add current module */
906 add_to_init_list( list, pModule );
908 /* Remove recursion flag */
909 pModule->misc_flags &= ~0x80;
911 TRACE_(dll)("(%s) - END\n", NE_MODULE_NAME(pModule) );
914 static void call_init_list( struct ne_init_list *list )
916 int i;
917 for ( i = 0; i < list->count; i++ )
918 NE_CallDllEntryPoint( list->module[i], DLL_PROCESS_ATTACH );
921 void NE_DllProcessAttach( HMODULE16 hModule )
923 struct ne_init_list list;
924 memset( &list, 0, sizeof(list) );
926 fill_init_list( &list, hModule );
927 call_init_list( &list );
928 free_init_list( &list );
932 /***********************************************************************
933 * NE_Ne2MemFlags
935 * This function translates NE segment flags to GlobalAlloc flags
937 static WORD NE_Ne2MemFlags(WORD flags)
939 WORD memflags = 0;
940 #if 1
941 if (flags & NE_SEGFLAGS_DISCARDABLE)
942 memflags |= GMEM_DISCARDABLE;
943 if (flags & NE_SEGFLAGS_MOVEABLE ||
944 ( ! (flags & NE_SEGFLAGS_DATA) &&
945 ! (flags & NE_SEGFLAGS_LOADED) &&
946 ! (flags & NE_SEGFLAGS_ALLOCATED)
949 memflags |= GMEM_MOVEABLE;
950 memflags |= GMEM_ZEROINIT;
951 #else
952 memflags = GMEM_ZEROINIT | GMEM_FIXED;
953 #endif
954 return memflags;
957 /***********************************************************************
958 * MyAlloc (KERNEL.668) Wine-specific export
960 * MyAlloc() function for self-loading apps.
962 DWORD WINAPI MyAlloc16( WORD wFlags, WORD wSize, WORD wElem )
964 WORD size = wSize << wElem;
965 HANDLE16 hMem = 0;
967 if (wSize || (wFlags & NE_SEGFLAGS_MOVEABLE))
968 hMem = GlobalAlloc16( NE_Ne2MemFlags(wFlags), size);
970 if ( ((wFlags & 0x7) != 0x1) && /* DATA */
971 ((wFlags & 0x7) != 0x7) ) /* DATA|ALLOCATED|LOADED */
973 WORD hSel = SEL(hMem);
974 WORD access = SelectorAccessRights16(hSel,0,0);
976 access |= 2<<2; /* SEGMENT_CODE */
977 SelectorAccessRights16(hSel,1,access);
979 if (size)
980 return MAKELONG( hMem, SEL(hMem) );
981 else
982 return MAKELONG( 0, hMem );
985 /***********************************************************************
986 * NE_GetInstance
988 HINSTANCE16 NE_GetInstance( NE_MODULE *pModule )
990 if ( !pModule->dgroup )
991 return pModule->self;
992 else
994 SEGTABLEENTRY *pSeg;
995 pSeg = NE_SEG_TABLE( pModule ) + pModule->dgroup - 1;
996 return pSeg->hSeg;
1000 /***********************************************************************
1001 * NE_CreateSegment
1003 BOOL NE_CreateSegment( NE_MODULE *pModule, int segnum )
1005 SEGTABLEENTRY *pSeg = NE_SEG_TABLE( pModule ) + segnum - 1;
1006 int minsize;
1007 unsigned char selflags;
1009 assert( !(pModule->flags & NE_FFLAGS_WIN32) );
1011 if ( segnum < 1 || segnum > pModule->seg_count )
1012 return FALSE;
1014 if ( (pModule->flags & NE_FFLAGS_SELFLOAD) && segnum != 1 )
1015 return TRUE; /* selfloader allocates segment itself */
1017 if ( (pSeg->flags & NE_SEGFLAGS_ALLOCATED) && segnum != pModule->dgroup )
1018 return TRUE; /* all but DGROUP only allocated once */
1020 minsize = pSeg->minsize ? pSeg->minsize : 0x10000;
1021 if ( segnum == pModule->ss ) minsize += pModule->stack_size;
1022 if ( segnum == pModule->dgroup ) minsize += pModule->heap_size;
1024 selflags = (pSeg->flags & NE_SEGFLAGS_DATA) ? WINE_LDT_FLAGS_DATA : WINE_LDT_FLAGS_CODE;
1025 if (pSeg->flags & NE_SEGFLAGS_32BIT) selflags |= WINE_LDT_FLAGS_32BIT;
1026 pSeg->hSeg = GLOBAL_Alloc( NE_Ne2MemFlags(pSeg->flags), minsize, pModule->self, selflags );
1027 if (!pSeg->hSeg) return FALSE;
1029 pSeg->flags |= NE_SEGFLAGS_ALLOCATED;
1030 return TRUE;
1033 /***********************************************************************
1034 * NE_CreateAllSegments
1036 BOOL NE_CreateAllSegments( NE_MODULE *pModule )
1038 int i;
1039 for ( i = 1; i <= pModule->seg_count; i++ )
1040 if ( !NE_CreateSegment( pModule, i ) )
1041 return FALSE;
1043 pModule->dgroup_entry = pModule->dgroup ? pModule->seg_table +
1044 (pModule->dgroup - 1) * sizeof(SEGTABLEENTRY) : 0;
1045 return TRUE;
1049 /**********************************************************************
1050 * IsSharedSelector (KERNEL.345)
1052 BOOL16 WINAPI IsSharedSelector16( HANDLE16 selector )
1054 /* Check whether the selector belongs to a DLL */
1055 NE_MODULE *pModule = NE_GetPtr( selector );
1056 if (!pModule) return FALSE;
1057 return (pModule->flags & NE_FFLAGS_LIBMODULE) != 0;