Fixed a typo in CryptExportKey.
[wine/hacks.git] / dlls / kernel / ne_segment.c
blobab4ea640f1ef37f80a845059cb7a84ac434f799d
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)[0];
215 unsigned int len = ((short*)curr)[1];
217 curr += 2*sizeof(short);
218 for(; rept > 0; rept--) {
219 char* bytes = curr;
220 unsigned int byte;
221 for(byte = 0; byte < len; byte++)
222 *mem++ = *bytes++;
224 curr += len;
226 HeapFree(GetProcessHeap(), 0, buff);
229 pSeg->flags |= NE_SEGFLAGS_LOADED;
231 /* Perform exported function prolog fixups */
232 NE_FixupSegmentPrologs( pModule, segnum );
234 if (!(pSeg->flags & NE_SEGFLAGS_RELOC_DATA))
235 goto succeed; /* No relocation data, we are done */
237 ReadFile(hf, &count, sizeof(count), &res, NULL);
238 if (!count) goto succeed;
240 TRACE("Fixups for %.*s, segment %d, hSeg %04x\n",
241 *((BYTE *)pModule + pModule->name_table),
242 (char *)pModule + pModule->name_table + 1,
243 segnum, pSeg->hSeg );
245 reloc_entries = (struct relocation_entry_s *)HeapAlloc(GetProcessHeap(), 0, count * sizeof(struct relocation_entry_s));
246 if(reloc_entries == NULL) {
247 WARN("Not enough memory for relocation entries!\n");
248 goto fail;
250 if (!ReadFile( hf, reloc_entries, count * sizeof(struct relocation_entry_s), &res, NULL) ||
251 (res != count * sizeof(struct relocation_entry_s)))
253 WARN("Unable to read relocation information\n" );
254 goto fail;
258 * Go through the relocation table one entry at a time.
260 rep = reloc_entries;
261 for (i = 0; i < count; i++, rep++)
264 * Get the target address corresponding to this entry.
267 /* If additive, there is no target chain list. Instead, add source
268 and target */
269 additive = rep->relocation_type & NE_RELFLAG_ADDITIVE;
270 rep->relocation_type &= 0x3;
272 switch (rep->relocation_type)
274 case NE_RELTYPE_ORDINAL:
275 module = pModuleTable[rep->target1-1];
276 ordinal = rep->target2;
277 address = NE_GetEntryPoint( module, ordinal );
278 if (!address)
280 NE_MODULE *pTarget = NE_GetPtr( module );
281 if (!pTarget)
282 WARN_(module)("Module not found: %04x, reference %d of module %*.*s\n",
283 module, rep->target1,
284 *((BYTE *)pModule + pModule->name_table),
285 *((BYTE *)pModule + pModule->name_table),
286 (char *)pModule + pModule->name_table + 1 );
287 else
289 ERR("No implementation for %.*s.%d, setting to 0xdeadbeef\n",
290 *((BYTE *)pTarget + pTarget->name_table),
291 (char *)pTarget + pTarget->name_table + 1,
292 ordinal );
293 address = (FARPROC16)0xdeadbeef;
296 if (TRACE_ON(fixup))
298 NE_MODULE *pTarget = NE_GetPtr( module );
299 TRACE("%d: %.*s.%d=%04x:%04x %s\n", i + 1,
300 *((BYTE *)pTarget + pTarget->name_table),
301 (char *)pTarget + pTarget->name_table + 1,
302 ordinal, HIWORD(address), LOWORD(address),
303 NE_GetRelocAddrName( rep->address_type, additive ) );
305 break;
307 case NE_RELTYPE_NAME:
308 module = pModuleTable[rep->target1-1];
309 func_name = (char *)pModule + pModule->import_table + rep->target2;
310 memcpy( buffer, func_name+1, *func_name );
311 buffer[*func_name] = '\0';
312 func_name = buffer;
313 ordinal = NE_GetOrdinal( module, func_name );
314 address = NE_GetEntryPoint( module, ordinal );
316 if (ERR_ON(fixup) && !address)
318 NE_MODULE *pTarget = NE_GetPtr( module );
319 ERR("No implementation for %.*s.%s, setting to 0xdeadbeef\n",
320 *((BYTE *)pTarget + pTarget->name_table),
321 (char *)pTarget + pTarget->name_table + 1, func_name );
323 if (!address) address = (FARPROC16) 0xdeadbeef;
324 if (TRACE_ON(fixup))
326 NE_MODULE *pTarget = NE_GetPtr( module );
327 TRACE("%d: %.*s.%s=%04x:%04x %s\n", i + 1,
328 *((BYTE *)pTarget + pTarget->name_table),
329 (char *)pTarget + pTarget->name_table + 1,
330 func_name, HIWORD(address), LOWORD(address),
331 NE_GetRelocAddrName( rep->address_type, additive ) );
333 break;
335 case NE_RELTYPE_INTERNAL:
336 if ((rep->target1 & 0xff) == 0xff)
338 address = NE_GetEntryPoint( pModule->self, rep->target2 );
340 else
342 address = (FARPROC16)MAKESEGPTR( SEL(pSegTable[rep->target1-1].hSeg), rep->target2 );
345 TRACE("%d: %04x:%04x %s\n",
346 i + 1, HIWORD(address), LOWORD(address),
347 NE_GetRelocAddrName( rep->address_type, additive ) );
348 break;
350 case NE_RELTYPE_OSFIXUP:
351 /* Relocation type 7:
353 * These appear to be used as fixups for the Windows
354 * floating point emulator. Let's just ignore them and
355 * try to use the hardware floating point. Linux should
356 * successfully emulate the coprocessor if it doesn't
357 * exist.
359 TRACE("%d: TYPE %d, OFFSET %04x, TARGET %04x %04x %s\n",
360 i + 1, rep->relocation_type, rep->offset,
361 rep->target1, rep->target2,
362 NE_GetRelocAddrName( rep->address_type, additive ) );
363 continue;
366 offset = rep->offset;
368 /* Apparently, high bit of address_type is sometimes set; */
369 /* we ignore it for now */
370 if (rep->address_type > NE_RADDR_OFFSET32)
372 char module[10];
373 GetModuleName16( pModule->self, module, sizeof(module) );
374 ERR("WARNING: module %s: unknown reloc addr type = 0x%02x. Please report.\n",
375 module, rep->address_type );
378 if (additive)
380 sp = MapSL( MAKESEGPTR( SEL(pSeg->hSeg), offset ) );
381 TRACE(" %04x:%04x\n", offset, *sp );
382 switch (rep->address_type & 0x7f)
384 case NE_RADDR_LOWBYTE:
385 *(BYTE *)sp += LOBYTE((int)address);
386 break;
387 case NE_RADDR_OFFSET16:
388 *sp += LOWORD(address);
389 break;
390 case NE_RADDR_POINTER32:
391 *sp += LOWORD(address);
392 *(sp+1) = HIWORD(address);
393 break;
394 case NE_RADDR_SELECTOR:
395 /* Borland creates additive records with offset zero. Strange, but OK */
396 if (*sp)
397 ERR("Additive selector to %04x.Please report\n",*sp);
398 else
399 *sp = HIWORD(address);
400 break;
401 default:
402 goto unknown;
405 else /* non-additive fixup */
409 sp = MapSL( MAKESEGPTR( SEL(pSeg->hSeg), offset ) );
410 next_offset = *sp;
411 TRACE(" %04x:%04x\n", offset, *sp );
412 switch (rep->address_type & 0x7f)
414 case NE_RADDR_LOWBYTE:
415 *(BYTE *)sp = LOBYTE((int)address);
416 break;
417 case NE_RADDR_OFFSET16:
418 *sp = LOWORD(address);
419 break;
420 case NE_RADDR_POINTER32:
421 *(FARPROC16 *)sp = address;
422 break;
423 case NE_RADDR_SELECTOR:
424 *sp = SELECTOROF(address);
425 break;
426 default:
427 goto unknown;
429 if (next_offset == offset) break; /* avoid infinite loop */
430 if (next_offset >= GlobalSize16(pSeg->hSeg)) break;
431 offset = next_offset;
432 } while (offset != 0xffff);
436 HeapFree(GetProcessHeap(), 0, reloc_entries);
438 succeed:
439 CloseHandle(hf);
440 return TRUE;
442 unknown:
443 WARN("WARNING: %d: unknown ADDR TYPE %d, "
444 "TYPE %d, OFFSET %04x, TARGET %04x %04x\n",
445 i + 1, rep->address_type, rep->relocation_type,
446 rep->offset, rep->target1, rep->target2);
447 HeapFree(GetProcessHeap(), 0, reloc_entries);
449 fail:
450 CloseHandle(hf);
451 return FALSE;
455 /***********************************************************************
456 * NE_LoadAllSegments
458 BOOL NE_LoadAllSegments( NE_MODULE *pModule )
460 int i;
461 SEGTABLEENTRY * pSegTable = (SEGTABLEENTRY *) NE_SEG_TABLE(pModule);
463 if (pModule->flags & NE_FFLAGS_SELFLOAD)
465 HANDLE hf;
466 HFILE16 hFile16;
467 HGLOBAL16 sel;
468 /* Handle self-loading modules */
469 SELFLOADHEADER *selfloadheader;
470 HMODULE16 mod = GetModuleHandle16("KERNEL");
471 DWORD oldstack;
472 WORD args[2];
474 TRACE_(module)("%.*s is a self-loading module!\n",
475 *((BYTE*)pModule + pModule->name_table),
476 (char *)pModule + pModule->name_table + 1);
477 if (!NE_LoadSegment( pModule, 1 )) return FALSE;
478 selfloadheader = MapSL( MAKESEGPTR(SEL(pSegTable->hSeg), 0) );
479 selfloadheader->EntryAddrProc = GetProcAddress16(mod,"EntryAddrProc");
480 selfloadheader->MyAlloc = GetProcAddress16(mod,"MyAlloc");
481 selfloadheader->SetOwner = GetProcAddress16(mod,"FarSetOwner");
482 sel = GlobalAlloc16( GMEM_ZEROINIT, 0xFF00 );
483 pModule->self_loading_sel = SEL(sel);
484 FarSetOwner16( sel, pModule->self );
485 oldstack = NtCurrentTeb()->cur_stack;
486 NtCurrentTeb()->cur_stack = MAKESEGPTR(pModule->self_loading_sel,
487 0xff00 - sizeof(STACK16FRAME) );
489 hf = NE_OpenFile(pModule);
490 hFile16 = Win32HandleToDosFileHandle( hf );
491 TRACE_(dll)("CallBootAppProc(hModule=0x%04x,hf=0x%04x)\n",
492 pModule->self,hFile16);
493 args[1] = pModule->self;
494 args[0] = hFile16;
495 WOWCallback16Ex( (DWORD)selfloadheader->BootApp, WCB16_PASCAL, sizeof(args), args, NULL );
496 TRACE_(dll)("Return from CallBootAppProc\n");
497 _lclose16(hFile16);
498 NtCurrentTeb()->cur_stack = oldstack;
500 for (i = 2; i <= pModule->seg_count; i++)
501 if (!NE_LoadSegment( pModule, i )) return FALSE;
503 else
505 for (i = 1; i <= pModule->seg_count; i++)
506 if (!NE_LoadSegment( pModule, i )) return FALSE;
508 return TRUE;
512 /***********************************************************************
513 * NE_FixupSegmentPrologs
515 * Fixup exported functions prologs of one segment
517 static void NE_FixupSegmentPrologs(NE_MODULE *pModule, WORD segnum)
519 SEGTABLEENTRY *pSegTable = NE_SEG_TABLE( pModule );
520 ET_BUNDLE *bundle;
521 ET_ENTRY *entry;
522 WORD dgroup, num_entries, sel = SEL(pSegTable[segnum-1].hSeg);
523 BYTE *pSeg, *pFunc;
525 TRACE("(%d);\n", segnum);
527 if (pSegTable[segnum-1].flags & NE_SEGFLAGS_DATA)
529 pSegTable[segnum-1].flags |= NE_SEGFLAGS_LOADED;
530 return;
533 if (!pModule->dgroup) return;
535 if (!(dgroup = SEL(pSegTable[pModule->dgroup-1].hSeg))) return;
537 pSeg = MapSL( MAKESEGPTR(sel, 0) );
539 bundle = (ET_BUNDLE *)((BYTE *)pModule+pModule->entry_table);
541 do {
542 TRACE("num_entries: %d, bundle: %p, next: %04x, pSeg: %p\n", bundle->last - bundle->first, bundle, bundle->next, pSeg);
543 if (!(num_entries = bundle->last - bundle->first))
544 return;
545 entry = (ET_ENTRY *)((BYTE *)bundle+6);
546 while (num_entries--)
548 /*TRACE("entry: %p, entry->segnum: %d, entry->offs: %04x\n", entry, entry->segnum, entry->offs);*/
549 if (entry->segnum == segnum)
551 pFunc = ((BYTE *)pSeg+entry->offs);
552 TRACE("pFunc: %p, *(DWORD *)pFunc: %08lx, num_entries: %d\n", pFunc, *(DWORD *)pFunc, num_entries);
553 if (*(pFunc+2) == 0x90)
555 if (*(WORD *)pFunc == 0x581e) /* push ds, pop ax */
557 TRACE("patch %04x:%04x -> mov ax, ds\n", sel, entry->offs);
558 *(WORD *)pFunc = 0xd88c; /* mov ax, ds */
561 if (*(WORD *)pFunc == 0xd88c)
563 if ((entry->flags & 2)) /* public data ? */
565 TRACE("patch %04x:%04x -> mov ax, dgroup [%04x]\n", sel, entry->offs, dgroup);
566 *pFunc = 0xb8; /* mov ax, */
567 *(WORD *)(pFunc+1) = dgroup;
569 else
570 if ((pModule->flags & NE_FFLAGS_MULTIPLEDATA)
571 && (entry->flags & 1)) /* exported ? */
573 TRACE("patch %04x:%04x -> nop, nop\n", sel, entry->offs);
574 *(WORD *)pFunc = 0x9090; /* nop, nop */
579 entry++;
581 } while ( (bundle->next)
582 && (bundle = ((ET_BUNDLE *)((BYTE *)pModule + bundle->next))) );
586 /***********************************************************************
587 * PatchCodeHandle (KERNEL.110)
589 * Needed for self-loading modules.
591 DWORD WINAPI PatchCodeHandle16(HANDLE16 hSeg)
593 WORD segnum;
594 WORD sel = SEL(hSeg);
595 NE_MODULE *pModule = NE_GetPtr(FarGetOwner16(sel));
596 SEGTABLEENTRY *pSegTable = NE_SEG_TABLE(pModule);
598 TRACE_(module)("(%04x);\n", hSeg);
600 /* find the segment number of the module that belongs to hSeg */
601 for (segnum = 1; segnum <= pModule->seg_count; segnum++)
603 if (SEL(pSegTable[segnum-1].hSeg) == sel)
605 NE_FixupSegmentPrologs(pModule, segnum);
606 break;
610 return MAKELONG(hSeg, sel);
614 /***********************************************************************
615 * NE_GetDLLInitParams
617 static VOID NE_GetDLLInitParams( NE_MODULE *pModule,
618 WORD *hInst, WORD *ds, WORD *heap )
620 SEGTABLEENTRY *pSegTable = NE_SEG_TABLE( pModule );
622 if (!(pModule->flags & NE_FFLAGS_SINGLEDATA))
624 if (pModule->flags & NE_FFLAGS_MULTIPLEDATA || pModule->dgroup)
626 /* Not SINGLEDATA */
627 ERR_(dll)("Library is not marked SINGLEDATA\n");
628 exit(1);
630 else /* DATA NONE DLL */
632 *ds = 0;
633 *heap = 0;
636 else /* DATA SINGLE DLL */
638 if (pModule->dgroup) {
639 *ds = SEL(pSegTable[pModule->dgroup-1].hSeg);
640 *heap = pModule->heap_size;
642 else /* hmm, DLL has no dgroup,
643 but why has it NE_FFLAGS_SINGLEDATA set ?
644 Buggy DLL compiler ? */
646 *ds = 0;
647 *heap = 0;
651 *hInst = *ds ? GlobalHandle16(*ds) : pModule->self;
655 /***********************************************************************
656 * NE_InitDLL
658 * Call the DLL initialization code
660 static BOOL NE_InitDLL( NE_MODULE *pModule )
662 SEGTABLEENTRY *pSegTable;
663 WORD hInst, ds, heap;
664 CONTEXT86 context;
666 pSegTable = NE_SEG_TABLE( pModule );
668 if (!(pModule->flags & NE_FFLAGS_LIBMODULE) ||
669 (pModule->flags & NE_FFLAGS_WIN32)) return TRUE; /*not a library*/
671 /* Call USER signal handler for Win3.1 compatibility. */
672 NE_CallUserSignalProc( pModule->self, USIG16_DLL_LOAD );
674 if (!pModule->cs) return TRUE; /* no initialization code */
677 /* Registers at initialization must be:
678 * cx heap size
679 * di library instance
680 * ds data segment if any
681 * es:si command line (always 0)
684 memset( &context, 0, sizeof(context) );
686 NE_GetDLLInitParams( pModule, &hInst, &ds, &heap );
688 context.Ecx = heap;
689 context.Edi = hInst;
690 context.SegDs = ds;
691 context.SegEs = ds; /* who knows ... */
692 context.SegFs = wine_get_fs();
693 context.SegGs = wine_get_gs();
694 context.SegCs = SEL(pSegTable[pModule->cs-1].hSeg);
695 context.Eip = pModule->ip;
696 context.Ebp = OFFSETOF(NtCurrentTeb()->cur_stack) + (WORD)&((STACK16FRAME*)0)->bp;
698 pModule->cs = 0; /* Don't initialize it twice */
699 TRACE_(dll)("Calling LibMain for %.*s, cs:ip=%04lx:%04lx ds=%04lx di=%04x cx=%04x\n",
700 *((BYTE*)pModule + pModule->name_table),
701 (char *)pModule + pModule->name_table + 1,
702 context.SegCs, context.Eip, context.SegDs,
703 LOWORD(context.Edi), LOWORD(context.Ecx) );
704 WOWCallback16Ex( 0, WCB16_REGS, 0, NULL, (DWORD *)&context );
705 return TRUE;
708 /***********************************************************************
709 * NE_InitializeDLLs
711 * Recursively initialize all DLLs (according to the order in which
712 * they where loaded).
714 void NE_InitializeDLLs( HMODULE16 hModule )
716 NE_MODULE *pModule;
717 HMODULE16 *pDLL;
719 if (!(pModule = NE_GetPtr( hModule ))) return;
720 assert( !(pModule->flags & NE_FFLAGS_WIN32) );
722 if (pModule->dlls_to_init)
724 HGLOBAL16 to_init = pModule->dlls_to_init;
725 pModule->dlls_to_init = 0;
726 for (pDLL = (HMODULE16 *)GlobalLock16( to_init ); *pDLL; pDLL++)
728 NE_InitializeDLLs( *pDLL );
730 GlobalFree16( to_init );
732 NE_InitDLL( pModule );
736 /**********************************************************************
737 * NE_CallUserSignalProc
739 * According to "Undocumented Windows", the task signal proc is
740 * bypassed for module load/unload notifications, and the USER signal
741 * proc is called directly instead. This is what this function does.
743 typedef DWORD (WINAPI *pSignalProc)( HANDLE16 module, UINT16 code, UINT16 exit,
744 HINSTANCE16 inst, HQUEUE16 queue );
746 void NE_CallUserSignalProc( HMODULE16 hModule, UINT16 code )
748 FARPROC16 proc;
749 HMODULE16 user = GetModuleHandle16("user.exe");
751 if (!user) return;
752 if ((proc = GetProcAddress16( user, "SignalProc" )))
754 /* USER is always a builtin dll */
755 pSignalProc sigproc = (pSignalProc)((ENTRYPOINT16 *)MapSL( (SEGPTR)proc ))->target;
756 sigproc( hModule, code, 0, 0, 0 );
761 /***********************************************************************
762 * NE_CallDllEntryPoint
764 * Call the DllEntryPoint of DLLs with subsystem >= 4.0
766 typedef DWORD (WINAPI *WinNEEntryProc)(DWORD,WORD,WORD,WORD,DWORD,WORD);
768 static void NE_CallDllEntryPoint( NE_MODULE *pModule, DWORD dwReason )
770 WORD hInst, ds, heap;
771 FARPROC16 entryPoint;
773 if (!(pModule->flags & NE_FFLAGS_LIBMODULE)) return;
774 if (!(pModule->flags & NE_FFLAGS_BUILTIN) && pModule->expected_version < 0x0400) return;
775 if (!(entryPoint = GetProcAddress16( pModule->self, "DllEntryPoint" ))) return;
777 NE_GetDLLInitParams( pModule, &hInst, &ds, &heap );
779 TRACE_(dll)( "Calling %s DllEntryPoint, cs:ip=%04x:%04x\n",
780 NE_MODULE_NAME( pModule ),
781 SELECTOROF(entryPoint), OFFSETOF(entryPoint) );
783 if ( pModule->flags & NE_FFLAGS_BUILTIN )
785 WinNEEntryProc entryProc = (WinNEEntryProc)((ENTRYPOINT16 *)MapSL( (SEGPTR)entryPoint ))->target;
787 entryProc( dwReason, hInst, ds, heap, 0, 0 );
789 else
791 CONTEXT86 context;
792 WORD args[8];
794 memset( &context, 0, sizeof(context) );
795 context.SegDs = ds;
796 context.SegEs = ds; /* who knows ... */
797 context.SegFs = wine_get_fs();
798 context.SegGs = wine_get_gs();
799 context.SegCs = HIWORD(entryPoint);
800 context.Eip = LOWORD(entryPoint);
801 context.Ebp = OFFSETOF( NtCurrentTeb()->cur_stack )
802 + (WORD)&((STACK16FRAME*)0)->bp;
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 );
816 /***********************************************************************
817 * NE_DllProcessAttach
819 * Call the DllEntryPoint of all modules this one (recursively)
820 * depends on, according to the order in which they were loaded.
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).
828 * Thus, we just call the DllEntryPoint twice in that case. Win9x
829 * appears to behave this way as well ...
831 * This routine must only be called with the Win16Lock held.
833 * FIXME: We should actually abort loading in case the DllEntryPoint
834 * returns FALSE ...
838 struct ne_init_list
840 int count;
841 int size;
842 NE_MODULE **module;
845 static void add_to_init_list( struct ne_init_list *list, NE_MODULE *hModule )
847 NE_MODULE **newModule = NULL;
848 if ( list->count == list->size )
850 int newSize = list->size + 128;
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 )
860 FIXME_(dll)("Out of memory!\n");
861 return;
864 list->module = newModule;
865 list->size = newSize;
868 list->module[list->count++] = hModule;
871 static void free_init_list( struct ne_init_list *list )
873 if ( list->module )
875 HeapFree( GetProcessHeap(), 0, list->module );
876 memset( list, 0, sizeof(*list) );
880 static void fill_init_list( struct ne_init_list *list, HMODULE16 hModule )
882 NE_MODULE *pModule;
883 HMODULE16 *pModRef;
884 int i;
886 if (!(pModule = NE_GetPtr( hModule ))) return;
887 assert( !(pModule->flags & NE_FFLAGS_WIN32) );
889 /* Never add a module twice */
890 for ( i = 0; i < list->count; i++ )
891 if ( list->module[i] == pModule )
892 return;
894 /* Check for recursive call */
895 if ( pModule->misc_flags & 0x80 ) return;
897 TRACE_(dll)("(%s) - START\n", NE_MODULE_NAME(pModule) );
899 /* Tag current module to prevent recursive loop */
900 pModule->misc_flags |= 0x80;
902 /* Recursively attach all DLLs this one depends on */
903 pModRef = (HMODULE16 *)((char *)pModule + pModule->modref_table);
904 for ( i = 0; i < pModule->modref_count; i++ )
905 if ( pModRef[i] ) fill_init_list( list, pModRef[i] );
907 /* Add current module */
908 add_to_init_list( list, pModule );
910 /* Remove recursion flag */
911 pModule->misc_flags &= ~0x80;
913 TRACE_(dll)("(%s) - END\n", NE_MODULE_NAME(pModule) );
916 static void call_init_list( struct ne_init_list *list )
918 int i;
919 for ( i = 0; i < list->count; i++ )
920 NE_CallDllEntryPoint( list->module[i], DLL_PROCESS_ATTACH );
923 void NE_DllProcessAttach( HMODULE16 hModule )
925 struct ne_init_list list;
926 memset( &list, 0, sizeof(list) );
928 fill_init_list( &list, hModule );
929 call_init_list( &list );
930 free_init_list( &list );
934 /***********************************************************************
935 * NE_Ne2MemFlags
937 * This function translates NE segment flags to GlobalAlloc flags
939 static WORD NE_Ne2MemFlags(WORD flags)
941 WORD memflags = 0;
942 #if 1
943 if (flags & NE_SEGFLAGS_DISCARDABLE)
944 memflags |= GMEM_DISCARDABLE;
945 if (flags & NE_SEGFLAGS_MOVEABLE ||
946 ( ! (flags & NE_SEGFLAGS_DATA) &&
947 ! (flags & NE_SEGFLAGS_LOADED) &&
948 ! (flags & NE_SEGFLAGS_ALLOCATED)
951 memflags |= GMEM_MOVEABLE;
952 memflags |= GMEM_ZEROINIT;
953 #else
954 memflags = GMEM_ZEROINIT | GMEM_FIXED;
955 #endif
956 return memflags;
959 /***********************************************************************
960 * MyAlloc (KERNEL.668) Wine-specific export
962 * MyAlloc() function for self-loading apps.
964 DWORD WINAPI MyAlloc16( WORD wFlags, WORD wSize, WORD wElem )
966 WORD size = wSize << wElem;
967 HANDLE16 hMem = 0;
969 if (wSize || (wFlags & NE_SEGFLAGS_MOVEABLE))
970 hMem = GlobalAlloc16( NE_Ne2MemFlags(wFlags), size);
972 if ( ((wFlags & 0x7) != 0x1) && /* DATA */
973 ((wFlags & 0x7) != 0x7) ) /* DATA|ALLOCATED|LOADED */
975 WORD hSel = SEL(hMem);
976 WORD access = SelectorAccessRights16(hSel,0,0);
978 access |= 2<<2; /* SEGMENT_CODE */
979 SelectorAccessRights16(hSel,1,access);
981 if (size)
982 return MAKELONG( hMem, SEL(hMem) );
983 else
984 return MAKELONG( 0, hMem );
987 /***********************************************************************
988 * NE_GetInstance
990 HINSTANCE16 NE_GetInstance( NE_MODULE *pModule )
992 if ( !pModule->dgroup )
993 return pModule->self;
994 else
996 SEGTABLEENTRY *pSeg;
997 pSeg = NE_SEG_TABLE( pModule ) + pModule->dgroup - 1;
998 return pSeg->hSeg;
1002 /***********************************************************************
1003 * NE_CreateSegment
1005 BOOL NE_CreateSegment( NE_MODULE *pModule, int segnum )
1007 SEGTABLEENTRY *pSeg = NE_SEG_TABLE( pModule ) + segnum - 1;
1008 int minsize;
1009 unsigned char selflags;
1011 assert( !(pModule->flags & NE_FFLAGS_WIN32) );
1013 if ( segnum < 1 || segnum > pModule->seg_count )
1014 return FALSE;
1016 if ( (pModule->flags & NE_FFLAGS_SELFLOAD) && segnum != 1 )
1017 return TRUE; /* selfloader allocates segment itself */
1019 if ( (pSeg->flags & NE_SEGFLAGS_ALLOCATED) && segnum != pModule->dgroup )
1020 return TRUE; /* all but DGROUP only allocated once */
1022 minsize = pSeg->minsize ? pSeg->minsize : 0x10000;
1023 if ( segnum == pModule->ss ) minsize += pModule->stack_size;
1024 if ( segnum == pModule->dgroup ) minsize += pModule->heap_size;
1026 selflags = (pSeg->flags & NE_SEGFLAGS_DATA) ? WINE_LDT_FLAGS_DATA : WINE_LDT_FLAGS_CODE;
1027 if (pSeg->flags & NE_SEGFLAGS_32BIT) selflags |= WINE_LDT_FLAGS_32BIT;
1028 pSeg->hSeg = GLOBAL_Alloc( NE_Ne2MemFlags(pSeg->flags), minsize, pModule->self, selflags );
1029 if (!pSeg->hSeg) return FALSE;
1031 pSeg->flags |= NE_SEGFLAGS_ALLOCATED;
1032 return TRUE;
1035 /***********************************************************************
1036 * NE_CreateAllSegments
1038 BOOL NE_CreateAllSegments( NE_MODULE *pModule )
1040 int i;
1041 for ( i = 1; i <= pModule->seg_count; i++ )
1042 if ( !NE_CreateSegment( pModule, i ) )
1043 return FALSE;
1045 pModule->dgroup_entry = pModule->dgroup ? pModule->seg_table +
1046 (pModule->dgroup - 1) * sizeof(SEGTABLEENTRY) : 0;
1047 return TRUE;
1051 /**********************************************************************
1052 * IsSharedSelector (KERNEL.345)
1054 BOOL16 WINAPI IsSharedSelector16( HANDLE16 selector )
1056 /* Check whether the selector belongs to a DLL */
1057 NE_MODULE *pModule = NE_GetPtr( selector );
1058 if (!pModule) return FALSE;
1059 return (pModule->flags & NE_FFLAGS_LIBMODULE) != 0;