Make sure dllname doesn't get optimized out (spotted by Marcus
[wine/multimedia.git] / loader / ne / segment.c
blob83fce736fd943ee026804d29596dd6e877a5acee
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 "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"
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 #define SEL(x) ((x)|1)
83 static void NE_FixupSegmentPrologs(NE_MODULE *pModule, WORD segnum);
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 ### */
90 /***********************************************************************
91 * NE_GetRelocAddrName
93 static const char *NE_GetRelocAddrName( BYTE addr_type, int additive )
95 switch(addr_type & 0x7f)
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";
104 return "???";
108 /***********************************************************************
109 * NE_LoadSegment
111 BOOL NE_LoadSegment( NE_MODULE *pModule, WORD segnum )
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;
125 char buffer[256];
126 int ordinal, additive;
127 unsigned short *sp;
129 pSegTable = NE_SEG_TABLE( pModule );
130 pSeg = pSegTable + segnum - 1;
132 if (pSeg->flags & NE_SEGFLAGS_LOADED)
134 /* self-loader ? -> already loaded it */
135 if (pModule->flags & NE_FFLAGS_SELFLOAD)
136 return TRUE;
138 /* leave, except for DGROUP, as this may be the second instance */
139 if (segnum != pModule->dgroup)
140 return TRUE;
143 if (!pSeg->filepos) return TRUE; /* No file image, just return */
145 pModuleTable = NE_MODULE_TABLE( pModule );
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)
156 /* Implement self-loading segments */
157 SELFLOADHEADER *selfloadheader;
158 DWORD oldstack;
159 HANDLE hFile32;
160 HFILE16 hFile16;
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));
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;
178 else if (!(pSeg->flags & NE_SEGFLAGS_ITERATED))
179 ReadFile(hf, mem, size, &res, NULL);
180 else {
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
187 char* buff = HeapAlloc(GetProcessHeap(), 0, size);
188 char* curr = buff;
190 if(buff == NULL) {
191 WARN_(dll)("Memory exausted!");
192 goto fail;
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++;
205 curr += len;
207 HeapFree(GetProcessHeap(), 0, buff);
210 pSeg->flags |= NE_SEGFLAGS_LOADED;
212 /* Perform exported function prolog fixups */
213 NE_FixupSegmentPrologs( pModule, segnum );
215 if (!(pSeg->flags & NE_SEGFLAGS_RELOC_DATA))
216 goto succeed; /* No relocation data, we are done */
218 ReadFile(hf, &count, sizeof(count), &res, NULL);
219 if (!count) goto succeed;
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 );
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;
231 if (!ReadFile( hf, reloc_entries, count * sizeof(struct relocation_entry_s), &res, NULL) ||
232 (res != count * sizeof(struct relocation_entry_s)))
234 WARN("Unable to read relocation information\n" );
235 goto fail;
239 * Go through the relocation table one entry at a time.
241 rep = reloc_entries;
242 for (i = 0; i < count; i++, rep++)
245 * Get the target address corresponding to this entry.
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;
253 switch (rep->relocation_type)
255 case NE_RELTYPE_ORDINAL:
256 module = pModuleTable[rep->target1-1];
257 ordinal = rep->target2;
258 address = NE_GetEntryPoint( module, ordinal );
259 if (!address)
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
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;
277 if (TRACE_ON(fixup))
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 ) );
286 break;
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 );
297 if (ERR_ON(fixup) && !address)
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 );
304 if (!address) address = (FARPROC16) 0xdeadbeef;
305 if (TRACE_ON(fixup))
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 ) );
314 break;
316 case NE_RELTYPE_INTERNAL:
317 if ((rep->target1 & 0xff) == 0xff)
319 address = NE_GetEntryPoint( pModule->self, rep->target2 );
321 else
323 address = (FARPROC16)MAKESEGPTR( SEL(pSegTable[rep->target1-1].hSeg), rep->target2 );
326 TRACE("%d: %04x:%04x %s\n",
327 i + 1, HIWORD(address), LOWORD(address),
328 NE_GetRelocAddrName( rep->address_type, additive ) );
329 break;
331 case NE_RELTYPE_OSFIXUP:
332 /* Relocation type 7:
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.
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;
347 offset = rep->offset;
349 /* Apparently, high bit of address_type is sometimes set; */
350 /* we ignore it for now */
351 if (rep->address_type > NE_RADDR_OFFSET32)
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 );
359 if (additive)
361 sp = MapSL( MAKESEGPTR( SEL(pSeg->hSeg), offset ) );
362 TRACE(" %04x:%04x\n", offset, *sp );
363 switch (rep->address_type & 0x7f)
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;
386 else /* non-additive fixup */
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)
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;
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);
417 HeapFree(GetProcessHeap(), 0, reloc_entries);
419 succeed:
420 CloseHandle(hf);
421 return TRUE;
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);
430 fail:
431 CloseHandle(hf);
432 return FALSE;
436 /***********************************************************************
437 * NE_LoadAllSegments
439 BOOL NE_LoadAllSegments( NE_MODULE *pModule )
441 int i;
442 SEGTABLEENTRY * pSegTable = (SEGTABLEENTRY *) NE_SEG_TABLE(pModule);
444 if (pModule->flags & NE_FFLAGS_SELFLOAD)
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;
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) );
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;
478 for (i = 2; i <= pModule->seg_count; i++)
479 if (!NE_LoadSegment( pModule, i )) return FALSE;
481 else
483 for (i = 1; i <= pModule->seg_count; i++)
484 if (!NE_LoadSegment( pModule, i )) return FALSE;
486 return TRUE;
490 /***********************************************************************
491 * NE_FixupSegmentPrologs
493 * Fixup exported functions prologs of one segment
495 static void NE_FixupSegmentPrologs(NE_MODULE *pModule, WORD segnum)
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;
503 TRACE("(%d);\n", segnum);
505 if (pSegTable[segnum-1].flags & NE_SEGFLAGS_DATA)
507 pSegTable[segnum-1].flags |= NE_SEGFLAGS_LOADED;
508 return;
511 if (!pModule->dgroup) return;
513 if (!(dgroup = SEL(pSegTable[pModule->dgroup-1].hSeg))) return;
515 pSeg = MapSL( MAKESEGPTR(sel, 0) );
517 bundle = (ET_BUNDLE *)((BYTE *)pModule+pModule->entry_table);
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--)
526 /*TRACE("entry: %p, entry->segnum: %d, entry->offs: %04x\n", entry, entry->segnum, entry->offs);*/
527 if (entry->segnum == segnum)
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)
533 if (*(WORD *)pFunc == 0x581e) /* push ds, pop ax */
535 TRACE("patch %04x:%04x -> mov ax, ds\n", sel, entry->offs);
536 *(WORD *)pFunc = 0xd88c; /* mov ax, ds */
539 if (*(WORD *)pFunc == 0xd88c)
541 if ((entry->flags & 2)) /* public data ? */
543 TRACE("patch %04x:%04x -> mov ax, dgroup [%04x]\n", sel, entry->offs, dgroup);
544 *pFunc = 0xb8; /* mov ax, */
545 *(WORD *)(pFunc+1) = dgroup;
547 else
548 if ((pModule->flags & NE_FFLAGS_MULTIPLEDATA)
549 && (entry->flags & 1)) /* exported ? */
551 TRACE("patch %04x:%04x -> nop, nop\n", sel, entry->offs);
552 *(WORD *)pFunc = 0x9090; /* nop, nop */
557 entry++;
559 } while ( (bundle->next)
560 && (bundle = ((ET_BUNDLE *)((BYTE *)pModule + bundle->next))) );
564 /***********************************************************************
565 * PatchCodeHandle (KERNEL.110)
567 * Needed for self-loading modules.
569 DWORD WINAPI PatchCodeHandle16(HANDLE16 hSeg)
571 WORD segnum;
572 WORD sel = SEL(hSeg);
573 NE_MODULE *pModule = NE_GetPtr(FarGetOwner16(sel));
574 SEGTABLEENTRY *pSegTable = NE_SEG_TABLE(pModule);
576 TRACE_(module)("(%04x);\n", hSeg);
578 /* find the segment number of the module that belongs to hSeg */
579 for (segnum = 1; segnum <= pModule->seg_count; segnum++)
581 if (SEL(pSegTable[segnum-1].hSeg) == sel)
583 NE_FixupSegmentPrologs(pModule, segnum);
584 break;
588 return MAKELONG(hSeg, sel);
592 /***********************************************************************
593 * NE_GetDLLInitParams
595 static VOID NE_GetDLLInitParams( NE_MODULE *pModule,
596 WORD *hInst, WORD *ds, WORD *heap )
598 SEGTABLEENTRY *pSegTable = NE_SEG_TABLE( pModule );
600 if (!(pModule->flags & NE_FFLAGS_SINGLEDATA))
602 if (pModule->flags & NE_FFLAGS_MULTIPLEDATA || pModule->dgroup)
604 /* Not SINGLEDATA */
605 ERR_(dll)("Library is not marked SINGLEDATA\n");
606 exit(1);
608 else /* DATA NONE DLL */
610 *ds = 0;
611 *heap = 0;
614 else /* DATA SINGLE DLL */
616 if (pModule->dgroup) {
617 *ds = SEL(pSegTable[pModule->dgroup-1].hSeg);
618 *heap = pModule->heap_size;
620 else /* hmm, DLL has no dgroup,
621 but why has it NE_FFLAGS_SINGLEDATA set ?
622 Buggy DLL compiler ? */
624 *ds = 0;
625 *heap = 0;
629 *hInst = *ds ? GlobalHandle16(*ds) : pModule->self;
633 /***********************************************************************
634 * NE_InitDLL
636 * Call the DLL initialization code
638 static BOOL NE_InitDLL( NE_MODULE *pModule )
640 SEGTABLEENTRY *pSegTable;
641 WORD hInst, ds, heap;
642 CONTEXT86 context;
644 pSegTable = NE_SEG_TABLE( pModule );
646 if (!(pModule->flags & NE_FFLAGS_LIBMODULE) ||
647 (pModule->flags & NE_FFLAGS_WIN32)) return TRUE; /*not a library*/
649 /* Call USER signal handler for Win3.1 compatibility. */
650 NE_CallUserSignalProc( pModule->self, USIG16_DLL_LOAD );
652 if (!pModule->cs) return TRUE; /* no initialization code */
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)
662 memset( &context, 0, sizeof(context) );
664 NE_GetDLLInitParams( pModule, &hInst, &ds, &heap );
666 context.Ecx = heap;
667 context.Edi = hInst;
668 context.SegDs = ds;
669 context.SegEs = ds; /* who knows ... */
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;
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;
684 /***********************************************************************
685 * NE_InitializeDLLs
687 * Recursively initialize all DLLs (according to the order in which
688 * they where loaded).
690 void NE_InitializeDLLs( HMODULE16 hModule )
692 NE_MODULE *pModule;
693 HMODULE16 *pDLL;
695 if (!(pModule = NE_GetPtr( hModule ))) return;
696 assert( !(pModule->flags & NE_FFLAGS_WIN32) );
698 if (pModule->dlls_to_init)
700 HGLOBAL16 to_init = pModule->dlls_to_init;
701 pModule->dlls_to_init = 0;
702 for (pDLL = (HMODULE16 *)GlobalLock16( to_init ); *pDLL; pDLL++)
704 NE_InitializeDLLs( *pDLL );
706 GlobalFree16( to_init );
708 NE_InitDLL( pModule );
712 /**********************************************************************
713 * NE_CallUserSignalProc
715 * According to "Undocumented Windows", the task signal proc is
716 * bypassed for module load/unload notifications, and the USER signal
717 * proc is called directly instead. This is what this function does.
719 typedef DWORD (WINAPI *pSignalProc)( HANDLE16 module, UINT16 code, UINT16 exit,
720 HINSTANCE16 inst, HQUEUE16 queue );
722 void NE_CallUserSignalProc( HMODULE16 hModule, UINT16 code )
724 FARPROC16 proc;
725 HMODULE16 user = GetModuleHandle16("user.exe");
727 if (!user) return;
728 if ((proc = GetProcAddress16( user, "SignalProc" )))
730 /* USER is always a builtin dll */
731 pSignalProc sigproc = (pSignalProc)((ENTRYPOINT16 *)MapSL( (SEGPTR)proc ))->target;
732 sigproc( hModule, code, 0, 0, 0 );
737 /***********************************************************************
738 * NE_CallDllEntryPoint
740 * Call the DllEntryPoint of DLLs with subsystem >= 4.0
742 typedef DWORD (WINAPI *WinNEEntryProc)(DWORD,WORD,WORD,WORD,DWORD,WORD);
744 static void NE_CallDllEntryPoint( NE_MODULE *pModule, DWORD dwReason )
746 WORD hInst, ds, heap;
747 FARPROC16 entryPoint;
749 if (!(pModule->flags & NE_FFLAGS_LIBMODULE)) return;
750 if (!(pModule->flags & NE_FFLAGS_BUILTIN) && pModule->expected_version < 0x0400) return;
751 if (!(entryPoint = GetProcAddress16( pModule->self, "DllEntryPoint" ))) return;
753 NE_GetDLLInitParams( pModule, &hInst, &ds, &heap );
755 TRACE_(dll)( "Calling %s DllEntryPoint, cs:ip=%04x:%04x\n",
756 NE_MODULE_NAME( pModule ),
757 SELECTOROF(entryPoint), OFFSETOF(entryPoint) );
759 if ( pModule->flags & NE_FFLAGS_BUILTIN )
761 WinNEEntryProc entryProc = (WinNEEntryProc)((ENTRYPOINT16 *)MapSL( (SEGPTR)entryPoint ))->target;
763 entryProc( dwReason, hInst, ds, heap, 0, 0 );
765 else
767 LPBYTE stack = (LPBYTE)CURRENT_STACK16;
768 CONTEXT86 context;
770 memset( &context, 0, sizeof(context) );
771 context.SegDs = ds;
772 context.SegEs = ds; /* who knows ... */
774 context.SegCs = HIWORD(entryPoint);
775 context.Eip = LOWORD(entryPoint);
776 context.Ebp = OFFSETOF( NtCurrentTeb()->cur_stack )
777 + (WORD)&((STACK16FRAME*)0)->bp;
779 *(DWORD *)(stack - 4) = dwReason; /* dwReason */
780 *(WORD *) (stack - 6) = hInst; /* hInst */
781 *(WORD *) (stack - 8) = ds; /* wDS */
782 *(WORD *) (stack - 10) = heap; /* wHeapSize */
783 *(DWORD *)(stack - 14) = 0; /* dwReserved1 */
784 *(WORD *) (stack - 16) = 0; /* wReserved2 */
786 wine_call_to_16_regs_short( &context, 16 );
790 /***********************************************************************
791 * NE_DllProcessAttach
793 * Call the DllEntryPoint of all modules this one (recursively)
794 * depends on, according to the order in which they were loaded.
796 * Note that --as opposed to the PE module case-- there is no notion
797 * of 'module loaded into a process' for NE modules, and hence we
798 * have no place to store the fact that the DllEntryPoint of a
799 * given module was already called on behalf of this process (e.g.
800 * due to some earlier LoadLibrary16 call).
802 * Thus, we just call the DllEntryPoint twice in that case. Win9x
803 * appears to behave this way as well ...
805 * This routine must only be called with the Win16Lock held.
807 * FIXME: We should actually abort loading in case the DllEntryPoint
808 * returns FALSE ...
812 struct ne_init_list
814 int count;
815 int size;
816 NE_MODULE **module;
819 static void add_to_init_list( struct ne_init_list *list, NE_MODULE *hModule )
821 if ( list->count == list->size )
823 int newSize = list->size + 128;
824 NE_MODULE **newModule = HeapReAlloc( GetProcessHeap(), 0,
825 list->module, newSize*sizeof(NE_MODULE *) );
826 if ( !newModule )
828 FIXME_(dll)("Out of memory!");
829 return;
832 list->module = newModule;
833 list->size = newSize;
836 list->module[list->count++] = hModule;
839 static void free_init_list( struct ne_init_list *list )
841 if ( list->module )
843 HeapFree( GetProcessHeap(), 0, list->module );
844 memset( list, 0, sizeof(*list) );
848 static void fill_init_list( struct ne_init_list *list, HMODULE16 hModule )
850 NE_MODULE *pModule;
851 WORD *pModRef;
852 int i;
854 if (!(pModule = NE_GetPtr( hModule ))) return;
855 assert( !(pModule->flags & NE_FFLAGS_WIN32) );
857 /* Never add a module twice */
858 for ( i = 0; i < list->count; i++ )
859 if ( list->module[i] == pModule )
860 return;
862 /* Check for recursive call */
863 if ( pModule->misc_flags & 0x80 ) return;
865 TRACE_(dll)("(%s) - START\n", NE_MODULE_NAME(pModule) );
867 /* Tag current module to prevent recursive loop */
868 pModule->misc_flags |= 0x80;
870 /* Recursively attach all DLLs this one depends on */
871 pModRef = NE_MODULE_TABLE( pModule );
872 for ( i = 0; i < pModule->modref_count; i++ )
873 if ( pModRef[i] )
874 fill_init_list( list, (HMODULE16)pModRef[i] );
876 /* Add current module */
877 add_to_init_list( list, pModule );
879 /* Remove recursion flag */
880 pModule->misc_flags &= ~0x80;
882 TRACE_(dll)("(%s) - END\n", NE_MODULE_NAME(pModule) );
885 static void call_init_list( struct ne_init_list *list )
887 int i;
888 for ( i = 0; i < list->count; i++ )
889 NE_CallDllEntryPoint( list->module[i], DLL_PROCESS_ATTACH );
892 void NE_DllProcessAttach( HMODULE16 hModule )
894 struct ne_init_list list;
895 memset( &list, 0, sizeof(list) );
897 fill_init_list( &list, hModule );
898 call_init_list( &list );
899 free_init_list( &list );
903 /***********************************************************************
904 * NE_Ne2MemFlags
906 * This function translates NE segment flags to GlobalAlloc flags
908 static WORD NE_Ne2MemFlags(WORD flags)
910 WORD memflags = 0;
911 #if 1
912 if (flags & NE_SEGFLAGS_DISCARDABLE)
913 memflags |= GMEM_DISCARDABLE;
914 if (flags & NE_SEGFLAGS_MOVEABLE ||
915 ( ! (flags & NE_SEGFLAGS_DATA) &&
916 ! (flags & NE_SEGFLAGS_LOADED) &&
917 ! (flags & NE_SEGFLAGS_ALLOCATED)
920 memflags |= GMEM_MOVEABLE;
921 memflags |= GMEM_ZEROINIT;
922 #else
923 memflags = GMEM_ZEROINIT | GMEM_FIXED;
924 #endif
925 return memflags;
928 /***********************************************************************
929 * MyAlloc (KERNEL.668) Wine-specific export
931 * MyAlloc() function for self-loading apps.
933 DWORD WINAPI MyAlloc16( WORD wFlags, WORD wSize, WORD wElem )
935 WORD size = wSize << wElem;
936 HANDLE16 hMem = 0;
938 if (wSize || (wFlags & NE_SEGFLAGS_MOVEABLE))
939 hMem = GlobalAlloc16( NE_Ne2MemFlags(wFlags), size);
941 if ( ((wFlags & 0x7) != 0x1) && /* DATA */
942 ((wFlags & 0x7) != 0x7) ) /* DATA|ALLOCATED|LOADED */
944 WORD hSel = SEL(hMem);
945 WORD access = SelectorAccessRights16(hSel,0,0);
947 access |= 2<<2; /* SEGMENT_CODE */
948 SelectorAccessRights16(hSel,1,access);
950 if (size)
951 return MAKELONG( hMem, SEL(hMem) );
952 else
953 return MAKELONG( 0, hMem );
956 /***********************************************************************
957 * NE_GetInstance
959 HINSTANCE16 NE_GetInstance( NE_MODULE *pModule )
961 if ( !pModule->dgroup )
962 return pModule->self;
963 else
965 SEGTABLEENTRY *pSeg;
966 pSeg = NE_SEG_TABLE( pModule ) + pModule->dgroup - 1;
967 return pSeg->hSeg;
971 /***********************************************************************
972 * NE_CreateSegment
974 BOOL NE_CreateSegment( NE_MODULE *pModule, int segnum )
976 SEGTABLEENTRY *pSeg = NE_SEG_TABLE( pModule ) + segnum - 1;
977 int minsize;
978 unsigned char selflags;
980 assert( !(pModule->flags & NE_FFLAGS_WIN32) );
982 if ( segnum < 1 || segnum > pModule->seg_count )
983 return FALSE;
985 if ( (pModule->flags & NE_FFLAGS_SELFLOAD) && segnum != 1 )
986 return TRUE; /* selfloader allocates segment itself */
988 if ( (pSeg->flags & NE_SEGFLAGS_ALLOCATED) && segnum != pModule->dgroup )
989 return TRUE; /* all but DGROUP only allocated once */
991 minsize = pSeg->minsize ? pSeg->minsize : 0x10000;
992 if ( segnum == pModule->ss ) minsize += pModule->stack_size;
993 if ( segnum == pModule->dgroup ) minsize += pModule->heap_size;
995 selflags = (pSeg->flags & NE_SEGFLAGS_DATA) ? WINE_LDT_FLAGS_DATA : WINE_LDT_FLAGS_CODE;
996 if (pSeg->flags & NE_SEGFLAGS_32BIT) selflags |= WINE_LDT_FLAGS_32BIT;
997 pSeg->hSeg = GLOBAL_Alloc( NE_Ne2MemFlags(pSeg->flags), minsize, pModule->self, selflags );
998 if (!pSeg->hSeg) return FALSE;
1000 pSeg->flags |= NE_SEGFLAGS_ALLOCATED;
1001 return TRUE;
1004 /***********************************************************************
1005 * NE_CreateAllSegments
1007 BOOL NE_CreateAllSegments( NE_MODULE *pModule )
1009 int i;
1010 for ( i = 1; i <= pModule->seg_count; i++ )
1011 if ( !NE_CreateSegment( pModule, i ) )
1012 return FALSE;
1014 pModule->dgroup_entry = pModule->dgroup ? pModule->seg_table +
1015 (pModule->dgroup - 1) * sizeof(SEGTABLEENTRY) : 0;
1016 return TRUE;
1020 /**********************************************************************
1021 * IsSharedSelector (KERNEL.345)
1023 BOOL16 WINAPI IsSharedSelector16( HANDLE16 selector )
1025 /* Check whether the selector belongs to a DLL */
1026 NE_MODULE *pModule = NE_GetPtr( selector );
1027 if (!pModule) return FALSE;
1028 return (pModule->flags & NE_FFLAGS_LIBMODULE) != 0;