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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 #include "wine/port.h"
28 #include <sys/types.h>
36 #include "wine/winbase16.h"
39 #include "wine/library.h"
40 #include "kernel16_private.h"
41 #include "wine/debug.h"
43 WINE_DEFAULT_DEBUG_CHANNEL(fixup
);
44 WINE_DECLARE_DEBUG_CHANNEL(dll
);
45 WINE_DECLARE_DEBUG_CHANNEL(module
);
48 * Relocation table entry
50 struct relocation_entry_s
52 BYTE address_type
; /* Relocation address type */
53 BYTE relocation_type
; /* Relocation type */
54 WORD offset
; /* Offset in segment to fixup */
55 WORD target1
; /* Target specification */
56 WORD target2
; /* Target specification */
60 * Relocation address types
62 #define NE_RADDR_LOWBYTE 0
63 #define NE_RADDR_SELECTOR 2
64 #define NE_RADDR_POINTER32 3
65 #define NE_RADDR_OFFSET16 5
66 #define NE_RADDR_POINTER48 11
67 #define NE_RADDR_OFFSET32 13
72 #define NE_RELTYPE_INTERNAL 0
73 #define NE_RELTYPE_ORDINAL 1
74 #define NE_RELTYPE_NAME 2
75 #define NE_RELTYPE_OSFIXUP 3
76 #define NE_RELFLAG_ADDITIVE 4
78 /* Self-loading modules contain this structure in their first segment */
81 WORD version
; /* Must be "A0" (0x3041) */
83 FARPROC16 BootApp
; /* startup procedure */
84 FARPROC16 LoadAppSeg
; /* procedure to load a segment */
86 FARPROC16 MyAlloc
; /* memory allocation procedure,
87 * wine must write this field */
88 FARPROC16 EntryAddrProc
;
89 FARPROC16 ExitProc
; /* exit procedure */
91 FARPROC16 SetOwner
; /* Set Owner procedure, exported by wine */
94 #define SEL(x) ((x)|1)
96 static void NE_FixupSegmentPrologs(NE_MODULE
*pModule
, WORD segnum
);
99 /***********************************************************************
100 * NE_GetRelocAddrName
102 static const char *NE_GetRelocAddrName( BYTE addr_type
, int additive
)
104 switch(addr_type
& 0x7f)
106 case NE_RADDR_LOWBYTE
: return additive
? "BYTE add" : "BYTE";
107 case NE_RADDR_OFFSET16
: return additive
? "OFFSET16 add" : "OFFSET16";
108 case NE_RADDR_POINTER32
: return additive
? "POINTER32 add" : "POINTER32";
109 case NE_RADDR_SELECTOR
: return additive
? "SELECTOR add" : "SELECTOR";
110 case NE_RADDR_POINTER48
: return additive
? "POINTER48 add" : "POINTER48";
111 case NE_RADDR_OFFSET32
: return additive
? "OFFSET32 add" : "OFFSET32";
117 /***********************************************************************
120 static HFILE16
NE_OpenFile( NE_MODULE
*pModule
)
122 char *name
= NE_MODULE_NAME( pModule
);
123 HANDLE handle
= CreateFileA( name
, GENERIC_READ
, FILE_SHARE_READ
,
124 NULL
, OPEN_EXISTING
, 0, 0 );
126 if (handle
== INVALID_HANDLE_VALUE
)
128 ERR( "Can't open file '%s' for module %04x\n", name
, pModule
->self
);
131 return Win32HandleToDosFileHandle( handle
);
135 /***********************************************************************
138 * Apply relocations to a segment. Helper for NE_LoadSegment.
140 static inline BOOL
apply_relocations( NE_MODULE
*pModule
, const struct relocation_entry_s
*rep
,
141 int count
, int segnum
)
148 FARPROC16 address
= 0;
149 HMODULE16
*pModuleTable
= (HMODULE16
*)((char *)pModule
+ pModule
->ne_modtab
);
150 SEGTABLEENTRY
*pSegTable
= NE_SEG_TABLE( pModule
);
151 SEGTABLEENTRY
*pSeg
= pSegTable
+ segnum
- 1;
154 * Go through the relocation table one entry at a time.
156 for (i
= 0; i
< count
; i
++, rep
++)
159 * Get the target address corresponding to this entry.
162 /* If additive, there is no target chain list. Instead, add source
164 int additive
= rep
->relocation_type
& NE_RELFLAG_ADDITIVE
;
165 switch (rep
->relocation_type
& 3)
167 case NE_RELTYPE_ORDINAL
:
168 module
= pModuleTable
[rep
->target1
-1];
169 ordinal
= rep
->target2
;
170 address
= NE_GetEntryPoint( module
, ordinal
);
173 NE_MODULE
*pTarget
= NE_GetPtr( module
);
175 WARN_(module
)("Module not found: %04x, reference %d of module %*.*s\n",
176 module
, rep
->target1
,
177 *((BYTE
*)pModule
+ pModule
->ne_restab
),
178 *((BYTE
*)pModule
+ pModule
->ne_restab
),
179 (char *)pModule
+ pModule
->ne_restab
+ 1 );
182 ERR("No implementation for %.*s.%d, setting to 0xdeadbeef\n",
183 *((BYTE
*)pTarget
+ pTarget
->ne_restab
),
184 (char *)pTarget
+ pTarget
->ne_restab
+ 1,
186 address
= (FARPROC16
)0xdeadbeef;
191 NE_MODULE
*pTarget
= NE_GetPtr( module
);
192 TRACE("%d: %.*s.%d=%04x:%04x %s\n", i
+ 1,
193 *((BYTE
*)pTarget
+ pTarget
->ne_restab
),
194 (char *)pTarget
+ pTarget
->ne_restab
+ 1,
195 ordinal
, HIWORD(address
), LOWORD(address
),
196 NE_GetRelocAddrName( rep
->address_type
, additive
) );
200 case NE_RELTYPE_NAME
:
201 module
= pModuleTable
[rep
->target1
-1];
202 func_name
= (BYTE
*)pModule
+ pModule
->ne_imptab
+ rep
->target2
;
203 memcpy( buffer
, func_name
+1, *func_name
);
204 buffer
[*func_name
] = '\0';
205 ordinal
= NE_GetOrdinal( module
, buffer
);
206 address
= NE_GetEntryPoint( module
, ordinal
);
208 if (ERR_ON(fixup
) && !address
)
210 NE_MODULE
*pTarget
= NE_GetPtr( module
);
211 ERR("No implementation for %.*s.%s, setting to 0xdeadbeef\n",
212 *((BYTE
*)pTarget
+ pTarget
->ne_restab
),
213 (char *)pTarget
+ pTarget
->ne_restab
+ 1, buffer
);
215 if (!address
) address
= (FARPROC16
) 0xdeadbeef;
218 NE_MODULE
*pTarget
= NE_GetPtr( module
);
219 TRACE("%d: %.*s.%s=%04x:%04x %s\n", i
+ 1,
220 *((BYTE
*)pTarget
+ pTarget
->ne_restab
),
221 (char *)pTarget
+ pTarget
->ne_restab
+ 1,
222 buffer
, HIWORD(address
), LOWORD(address
),
223 NE_GetRelocAddrName( rep
->address_type
, additive
) );
227 case NE_RELTYPE_INTERNAL
:
228 if ((rep
->target1
& 0xff) == 0xff)
230 address
= NE_GetEntryPoint( pModule
->self
, rep
->target2
);
234 address
= (FARPROC16
)MAKESEGPTR( SEL(pSegTable
[rep
->target1
-1].hSeg
), rep
->target2
);
237 TRACE("%d: %04x:%04x %s\n",
238 i
+ 1, HIWORD(address
), LOWORD(address
),
239 NE_GetRelocAddrName( rep
->address_type
, additive
) );
242 case NE_RELTYPE_OSFIXUP
:
243 /* Relocation type 7:
245 * These appear to be used as fixups for the Windows
246 * floating point emulator. Let's just ignore them and
247 * try to use the hardware floating point. Linux should
248 * successfully emulate the coprocessor if it doesn't
251 TRACE("%d: TYPE %d, OFFSET %04x, TARGET %04x %04x %s\n",
252 i
+ 1, rep
->relocation_type
, rep
->offset
,
253 rep
->target1
, rep
->target2
,
254 NE_GetRelocAddrName( rep
->address_type
, additive
) );
258 offset
= rep
->offset
;
260 /* Apparently, high bit of address_type is sometimes set; */
261 /* we ignore it for now */
262 if (rep
->address_type
> NE_RADDR_OFFSET32
)
265 GetModuleName16( pModule
->self
, module
, sizeof(module
) );
266 ERR("WARNING: module %s: unknown reloc addr type = 0x%02x. Please report.\n",
267 module
, rep
->address_type
);
272 sp
= MapSL( MAKESEGPTR( SEL(pSeg
->hSeg
), offset
) );
273 TRACE(" %04x:%04x\n", offset
, *sp
);
274 switch (rep
->address_type
& 0x7f)
276 case NE_RADDR_LOWBYTE
:
277 *(BYTE
*)sp
+= LOBYTE((int)address
);
279 case NE_RADDR_OFFSET16
:
280 *sp
+= LOWORD(address
);
282 case NE_RADDR_POINTER32
:
283 *sp
+= LOWORD(address
);
284 *(sp
+1) = HIWORD(address
);
286 case NE_RADDR_SELECTOR
:
287 /* Borland creates additive records with offset zero. Strange, but OK */
289 ERR("Additive selector to %04x.Please report\n",*sp
);
291 *sp
= HIWORD(address
);
297 else /* non-additive fixup */
303 sp
= MapSL( MAKESEGPTR( SEL(pSeg
->hSeg
), offset
) );
305 TRACE(" %04x:%04x\n", offset
, *sp
);
306 switch (rep
->address_type
& 0x7f)
308 case NE_RADDR_LOWBYTE
:
309 *(BYTE
*)sp
= LOBYTE((int)address
);
311 case NE_RADDR_OFFSET16
:
312 *sp
= LOWORD(address
);
314 case NE_RADDR_POINTER32
:
315 *(FARPROC16
*)sp
= address
;
317 case NE_RADDR_SELECTOR
:
318 *sp
= SELECTOROF(address
);
323 if (next_offset
== offset
) break; /* avoid infinite loop */
324 if (next_offset
>= GlobalSize16(pSeg
->hSeg
)) break;
325 offset
= next_offset
;
326 } while (offset
!= 0xffff);
332 WARN("WARNING: %d: unknown ADDR TYPE %d, "
333 "TYPE %d, OFFSET %04x, TARGET %04x %04x\n",
334 i
+ 1, rep
->address_type
, rep
->relocation_type
,
335 rep
->offset
, rep
->target1
, rep
->target2
);
340 /***********************************************************************
343 BOOL
NE_LoadSegment( NE_MODULE
*pModule
, WORD segnum
)
347 const struct relocation_entry_s
*rep
;
349 SEGTABLEENTRY
*pSegTable
= NE_SEG_TABLE( pModule
);
350 SEGTABLEENTRY
*pSeg
= pSegTable
+ segnum
- 1;
352 if (pSeg
->flags
& NE_SEGFLAGS_LOADED
)
354 /* self-loader ? -> already loaded it */
355 if (pModule
->ne_flags
& NE_FFLAGS_SELFLOAD
)
358 /* leave, except for DGROUP, as this may be the second instance */
359 if (segnum
!= pModule
->ne_autodata
)
363 if (!pSeg
->filepos
) return TRUE
; /* No file image, just return */
365 TRACE_(module
)("Loading segment %d, hSeg=%04x, flags=%04x\n",
366 segnum
, pSeg
->hSeg
, pSeg
->flags
);
367 pos
= pSeg
->filepos
<< pModule
->ne_align
;
368 if (pSeg
->size
) size
= pSeg
->size
;
369 else size
= pSeg
->minsize
? pSeg
->minsize
: 0x10000;
371 if (pModule
->ne_flags
& NE_FFLAGS_SELFLOAD
&& segnum
> 1)
373 /* Implement self-loading segments */
374 SELFLOADHEADER
*selfloadheader
;
380 selfloadheader
= MapSL( MAKESEGPTR(SEL(pSegTable
->hSeg
),0) );
381 oldstack
= NtCurrentTeb()->WOW32Reserved
;
382 NtCurrentTeb()->WOW32Reserved
= (void *)MAKESEGPTR(pModule
->self_loading_sel
,
383 0xff00 - sizeof(STACK16FRAME
));
385 hFile16
= NE_OpenFile( pModule
);
386 TRACE_(dll
)("Call LoadAppSegProc(hmodule=0x%04x,hf=%x,segnum=%d)\n",
387 pModule
->self
,hFile16
,segnum
);
388 args
[2] = pModule
->self
;
391 WOWCallback16Ex( (DWORD
)selfloadheader
->LoadAppSeg
, WCB16_PASCAL
, sizeof(args
), args
, &ret
);
392 pSeg
->hSeg
= LOWORD(ret
);
393 TRACE_(dll
)("Ret LoadAppSegProc: hSeg=0x%04x\n", pSeg
->hSeg
);
394 _lclose16( hFile16
);
395 NtCurrentTeb()->WOW32Reserved
= oldstack
;
397 pSeg
->flags
|= NE_SEGFLAGS_LOADED
;
400 else if (!(pSeg
->flags
& NE_SEGFLAGS_ITERATED
))
402 void *mem
= GlobalLock16(pSeg
->hSeg
);
403 if (!NE_READ_DATA( pModule
, mem
, pos
, size
))
410 The following bit of code for "iterated segments" was written without
411 any documentation on the format of these segments. It seems to work,
412 but may be missing something.
414 const char *buff
= NE_GET_DATA( pModule
, pos
, size
);
415 const char* curr
= buff
;
416 char *mem
= GlobalLock16(pSeg
->hSeg
);
419 if (buff
== NULL
) return FALSE
;
421 while(curr
< buff
+ size
) {
422 unsigned int rept
= ((const short *)curr
)[0];
423 unsigned int len
= ((const short *)curr
)[1];
425 curr
+= 2*sizeof(short);
428 memcpy( mem
, curr
, len
);
435 pSeg
->flags
|= NE_SEGFLAGS_LOADED
;
437 /* Perform exported function prolog fixups */
438 NE_FixupSegmentPrologs( pModule
, segnum
);
440 if (!(pSeg
->flags
& NE_SEGFLAGS_RELOC_DATA
))
441 return TRUE
; /* No relocation data, we are done */
443 if (!NE_READ_DATA( pModule
, &count
, pos
, sizeof(count
) ) || !count
) return TRUE
;
444 pos
+= sizeof(count
);
446 TRACE("Fixups for %.*s, segment %d, hSeg %04x\n",
447 *((BYTE
*)pModule
+ pModule
->ne_restab
),
448 (char *)pModule
+ pModule
->ne_restab
+ 1,
449 segnum
, pSeg
->hSeg
);
451 if (!(rep
= NE_GET_DATA( pModule
, pos
, count
* sizeof(struct relocation_entry_s
) )))
454 return apply_relocations( pModule
, rep
, count
, segnum
);
458 /***********************************************************************
461 BOOL
NE_LoadAllSegments( NE_MODULE
*pModule
)
464 SEGTABLEENTRY
* pSegTable
= NE_SEG_TABLE(pModule
);
466 if (pModule
->ne_flags
& NE_FFLAGS_SELFLOAD
)
470 /* Handle self-loading modules */
471 SELFLOADHEADER
*selfloadheader
;
472 HMODULE16 mod
= GetModuleHandle16("KERNEL");
476 TRACE_(module
)("%.*s is a self-loading module!\n",
477 *((BYTE
*)pModule
+ pModule
->ne_restab
),
478 (char *)pModule
+ pModule
->ne_restab
+ 1);
479 if (!NE_LoadSegment( pModule
, 1 )) return FALSE
;
480 selfloadheader
= MapSL( MAKESEGPTR(SEL(pSegTable
->hSeg
), 0) );
481 selfloadheader
->EntryAddrProc
= GetProcAddress16(mod
,"EntryAddrProc");
482 selfloadheader
->MyAlloc
= GetProcAddress16(mod
,"MyAlloc");
483 selfloadheader
->SetOwner
= GetProcAddress16(mod
,"FarSetOwner");
484 sel
= GlobalAlloc16( GMEM_ZEROINIT
, 0xFF00 );
485 pModule
->self_loading_sel
= SEL(sel
);
486 FarSetOwner16( sel
, pModule
->self
);
487 oldstack
= NtCurrentTeb()->WOW32Reserved
;
488 NtCurrentTeb()->WOW32Reserved
= (void *)MAKESEGPTR(pModule
->self_loading_sel
,
489 0xff00 - sizeof(STACK16FRAME
) );
491 hFile16
= NE_OpenFile(pModule
);
492 TRACE_(dll
)("CallBootAppProc(hModule=0x%04x,hf=0x%04x)\n",
493 pModule
->self
,hFile16
);
494 args
[1] = pModule
->self
;
496 WOWCallback16Ex( (DWORD
)selfloadheader
->BootApp
, WCB16_PASCAL
, sizeof(args
), args
, NULL
);
497 TRACE_(dll
)("Return from CallBootAppProc\n");
499 NtCurrentTeb()->WOW32Reserved
= oldstack
;
501 for (i
= 2; i
<= pModule
->ne_cseg
; i
++)
502 if (!NE_LoadSegment( pModule
, i
)) return FALSE
;
506 for (i
= 1; i
<= pModule
->ne_cseg
; i
++)
507 if (!NE_LoadSegment( pModule
, i
)) return FALSE
;
513 /***********************************************************************
514 * NE_FixupSegmentPrologs
516 * Fixup exported functions prologs of one segment
518 static void NE_FixupSegmentPrologs(NE_MODULE
*pModule
, WORD segnum
)
520 SEGTABLEENTRY
*pSegTable
= NE_SEG_TABLE( pModule
);
523 WORD dgroup
, num_entries
, sel
= SEL(pSegTable
[segnum
-1].hSeg
);
526 TRACE("(%d);\n", segnum
);
528 if (pSegTable
[segnum
-1].flags
& NE_SEGFLAGS_DATA
)
530 pSegTable
[segnum
-1].flags
|= NE_SEGFLAGS_LOADED
;
534 if (!pModule
->ne_autodata
) return;
536 if (!pSegTable
[pModule
->ne_autodata
-1].hSeg
) return;
537 dgroup
= SEL(pSegTable
[pModule
->ne_autodata
-1].hSeg
);
539 pSeg
= MapSL( MAKESEGPTR(sel
, 0) );
541 bundle
= (ET_BUNDLE
*)((BYTE
*)pModule
+pModule
->ne_enttab
);
544 TRACE("num_entries: %d, bundle: %p, next: %04x, pSeg: %p\n", bundle
->last
- bundle
->first
, bundle
, bundle
->next
, pSeg
);
545 if (!(num_entries
= bundle
->last
- bundle
->first
))
547 entry
= (ET_ENTRY
*)((BYTE
*)bundle
+6);
548 while (num_entries
--)
550 /*TRACE("entry: %p, entry->segnum: %d, entry->offs: %04x\n", entry, entry->segnum, entry->offs);*/
551 if (entry
->segnum
== segnum
)
553 pFunc
= pSeg
+entry
->offs
;
554 TRACE("pFunc: %p, *(DWORD *)pFunc: %08x, num_entries: %d\n", pFunc
, *(DWORD
*)pFunc
, num_entries
);
555 if (*(pFunc
+2) == 0x90)
557 if (*(WORD
*)pFunc
== 0x581e) /* push ds, pop ax */
559 TRACE("patch %04x:%04x -> mov ax, ds\n", sel
, entry
->offs
);
560 *(WORD
*)pFunc
= 0xd88c; /* mov ax, ds */
563 if (*(WORD
*)pFunc
== 0xd88c)
565 if ((entry
->flags
& 2)) /* public data ? */
567 TRACE("patch %04x:%04x -> mov ax, dgroup [%04x]\n", sel
, entry
->offs
, dgroup
);
568 *pFunc
= 0xb8; /* mov ax, */
569 *(WORD
*)(pFunc
+1) = dgroup
;
571 else if ((pModule
->ne_flags
& NE_FFLAGS_MULTIPLEDATA
)
572 && (entry
->flags
& 1)) /* exported ? */
574 TRACE("patch %04x:%04x -> nop, nop\n", sel
, entry
->offs
);
575 *(WORD
*)pFunc
= 0x9090; /* nop, nop */
582 } while ( (bundle
->next
) && (bundle
= ((ET_BUNDLE
*)((BYTE
*)pModule
+ bundle
->next
))) );
586 /***********************************************************************
587 * PatchCodeHandle (KERNEL.110)
589 * Needed for self-loading modules.
591 DWORD WINAPI
PatchCodeHandle16(HANDLE16 hSeg
)
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
->ne_cseg
; segnum
++)
603 if (SEL(pSegTable
[segnum
-1].hSeg
) == sel
)
605 NE_FixupSegmentPrologs(pModule
, segnum
);
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
->ne_flags
& NE_FFLAGS_SINGLEDATA
))
624 if (pModule
->ne_flags
& NE_FFLAGS_MULTIPLEDATA
|| pModule
->ne_autodata
)
627 ERR_(dll
)("Library is not marked SINGLEDATA\n");
630 else /* DATA NONE DLL */
636 else /* DATA SINGLE DLL */
638 if (pModule
->ne_autodata
) {
639 *ds
= SEL(pSegTable
[pModule
->ne_autodata
-1].hSeg
);
640 *heap
= pModule
->ne_heap
;
642 else /* hmm, DLL has no dgroup,
643 but why has it NE_FFLAGS_SINGLEDATA set ?
644 Buggy DLL compiler ? */
651 *hInst
= *ds
? GlobalHandle16(*ds
) : pModule
->self
;
655 /***********************************************************************
658 * Call the DLL initialization code
660 static BOOL
NE_InitDLL( NE_MODULE
*pModule
)
662 SEGTABLEENTRY
*pSegTable
;
663 WORD hInst
, ds
, heap
;
666 pSegTable
= NE_SEG_TABLE( pModule
);
668 if (!(pModule
->ne_flags
& NE_FFLAGS_LIBMODULE
) ||
669 (pModule
->ne_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 (!SELECTOROF(pModule
->ne_csip
)) return TRUE
; /* no initialization code */
677 /* Registers at initialization must be:
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
);
691 context
.SegEs
= ds
; /* who knows ... */
692 context
.SegFs
= wine_get_fs();
693 context
.SegGs
= wine_get_gs();
694 context
.SegCs
= SEL(pSegTable
[SELECTOROF(pModule
->ne_csip
)-1].hSeg
);
695 context
.Eip
= OFFSETOF(pModule
->ne_csip
);
696 context
.Ebp
= OFFSETOF(NtCurrentTeb()->WOW32Reserved
) + FIELD_OFFSET(STACK16FRAME
,bp
);
698 pModule
->ne_csip
= 0; /* Don't initialize it twice */
699 TRACE_(dll
)("Calling LibMain for %.*s, cs:ip=%04x:%04x ds=%04x di=%04x cx=%04x\n",
700 *((BYTE
*)pModule
+ pModule
->ne_restab
),
701 (char *)pModule
+ pModule
->ne_restab
+ 1,
702 context
.SegCs
, context
.Eip
, context
.SegDs
,
703 LOWORD(context
.Edi
), LOWORD(context
.Ecx
) );
704 WOWCallback16Ex( 0, WCB16_REGS
, 0, NULL
, (DWORD
*)&context
);
708 /***********************************************************************
711 * Recursively initialize all DLLs (according to the order in which
712 * they where loaded).
714 void NE_InitializeDLLs( HMODULE16 hModule
)
719 if (!(pModule
= NE_GetPtr( hModule
))) return;
720 assert( !(pModule
->ne_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
= 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
)
749 HMODULE16 user
= GetModuleHandle16("user.exe");
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
->ne_flags
& NE_FFLAGS_LIBMODULE
)) return;
774 if (!(pModule
->ne_flags
& NE_FFLAGS_BUILTIN
) && pModule
->ne_expver
< 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
->ne_flags
& NE_FFLAGS_BUILTIN
)
785 WinNEEntryProc entryProc
= (WinNEEntryProc
)((ENTRYPOINT16
*)MapSL( (SEGPTR
)entryPoint
))->target
;
787 entryProc( dwReason
, hInst
, ds
, heap
, 0, 0 );
794 memset( &context
, 0, sizeof(context
) );
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()->WOW32Reserved
) + FIELD_OFFSET(STACK16FRAME
,bp
);
803 args
[7] = HIWORD(dwReason
);
804 args
[6] = LOWORD(dwReason
);
808 args
[2] = 0; /* HIWORD(dwReserved1) */
809 args
[1] = 0; /* LOWORD(dwReserved1) */
810 args
[0] = 0; /* wReserved2 */
811 WOWCallback16Ex( 0, WCB16_REGS
, sizeof(args
), args
, (DWORD
*)&context
);
815 /***********************************************************************
816 * NE_DllProcessAttach
818 * Call the DllEntryPoint of all modules this one (recursively)
819 * depends on, according to the order in which they were loaded.
821 * Note that --as opposed to the PE module case-- there is no notion
822 * of 'module loaded into a process' for NE modules, and hence we
823 * have no place to store the fact that the DllEntryPoint of a
824 * given module was already called on behalf of this process (e.g.
825 * due to some earlier LoadLibrary16 call).
827 * Thus, we just call the DllEntryPoint twice in that case. Win9x
828 * appears to behave this way as well ...
830 * This routine must only be called with the Win16Lock held.
832 * FIXME: We should actually abort loading in case the DllEntryPoint
844 static void add_to_init_list( struct ne_init_list
*list
, NE_MODULE
*hModule
)
846 NE_MODULE
**newModule
= NULL
;
847 if ( list
->count
== list
->size
)
849 int newSize
= list
->size
+ 128;
852 newModule
= HeapReAlloc( GetProcessHeap(), 0,
853 list
->module
, newSize
*sizeof(NE_MODULE
*) );
855 newModule
= HeapAlloc( GetProcessHeap(), 0,
856 newSize
*sizeof(NE_MODULE
*) );
859 FIXME_(dll
)("Out of memory!\n");
863 list
->module
= newModule
;
864 list
->size
= newSize
;
867 list
->module
[list
->count
++] = hModule
;
870 static void free_init_list( struct ne_init_list
*list
)
874 HeapFree( GetProcessHeap(), 0, list
->module
);
875 memset( list
, 0, sizeof(*list
) );
879 static void fill_init_list( struct ne_init_list
*list
, HMODULE16 hModule
)
885 if (!(pModule
= NE_GetPtr( hModule
))) return;
886 assert( !(pModule
->ne_flags
& NE_FFLAGS_WIN32
) );
888 /* Never add a module twice */
889 for ( i
= 0; i
< list
->count
; i
++ )
890 if ( list
->module
[i
] == pModule
)
893 /* Check for recursive call */
894 if ( pModule
->ne_flagsothers
& 0x80 ) return;
896 TRACE_(dll
)("(%s) - START\n", NE_MODULE_NAME(pModule
) );
898 /* Tag current module to prevent recursive loop */
899 pModule
->ne_flagsothers
|= 0x80;
901 /* Recursively attach all DLLs this one depends on */
902 pModRef
= (HMODULE16
*)((char *)pModule
+ pModule
->ne_modtab
);
903 for ( i
= 0; i
< pModule
->ne_cmod
; i
++ )
904 if ( pModRef
[i
] ) fill_init_list( list
, pModRef
[i
] );
906 /* Add current module */
907 add_to_init_list( list
, pModule
);
909 /* Remove recursion flag */
910 pModule
->ne_flagsothers
&= ~0x80;
912 TRACE_(dll
)("(%s) - END\n", NE_MODULE_NAME(pModule
) );
915 static void call_init_list( struct ne_init_list
*list
)
918 for ( i
= 0; i
< list
->count
; i
++ )
919 NE_CallDllEntryPoint( list
->module
[i
], DLL_PROCESS_ATTACH
);
922 void NE_DllProcessAttach( HMODULE16 hModule
)
924 struct ne_init_list list
;
925 memset( &list
, 0, sizeof(list
) );
927 fill_init_list( &list
, hModule
);
928 call_init_list( &list
);
929 free_init_list( &list
);
933 /***********************************************************************
936 * This function translates NE segment flags to GlobalAlloc flags
938 static WORD
NE_Ne2MemFlags(WORD flags
)
942 if (flags
& NE_SEGFLAGS_DISCARDABLE
)
943 memflags
|= GMEM_DISCARDABLE
;
944 if (flags
& NE_SEGFLAGS_MOVEABLE
||
945 ( ! (flags
& NE_SEGFLAGS_DATA
) &&
946 ! (flags
& NE_SEGFLAGS_LOADED
) &&
947 ! (flags
& NE_SEGFLAGS_ALLOCATED
)
950 memflags
|= GMEM_MOVEABLE
;
951 memflags
|= GMEM_ZEROINIT
;
953 memflags
= GMEM_ZEROINIT
| GMEM_FIXED
;
958 /***********************************************************************
959 * MyAlloc (KERNEL.668) Wine-specific export
961 * MyAlloc() function for self-loading apps.
963 DWORD WINAPI
MyAlloc16( WORD wFlags
, WORD wSize
, WORD wElem
)
965 WORD size
= wSize
<< wElem
;
968 if (wSize
|| (wFlags
& NE_SEGFLAGS_MOVEABLE
))
969 hMem
= GlobalAlloc16( NE_Ne2MemFlags(wFlags
), size
);
971 if ( ((wFlags
& 0x7) != 0x1) && /* DATA */
972 ((wFlags
& 0x7) != 0x7) ) /* DATA|ALLOCATED|LOADED */
974 WORD hSel
= SEL(hMem
);
975 WORD access
= SelectorAccessRights16(hSel
,0,0);
977 access
|= 2<<2; /* SEGMENT_CODE */
978 SelectorAccessRights16(hSel
,1,access
);
981 return MAKELONG( hMem
, SEL(hMem
) );
983 return MAKELONG( 0, hMem
);
986 /***********************************************************************
989 HINSTANCE16
NE_GetInstance( NE_MODULE
*pModule
)
991 if ( !pModule
->ne_autodata
)
992 return pModule
->self
;
996 pSeg
= NE_SEG_TABLE( pModule
) + pModule
->ne_autodata
- 1;
1001 /***********************************************************************
1004 BOOL
NE_CreateSegment( NE_MODULE
*pModule
, int segnum
)
1006 SEGTABLEENTRY
*pSeg
= NE_SEG_TABLE( pModule
) + segnum
- 1;
1008 unsigned char selflags
;
1010 assert( !(pModule
->ne_flags
& NE_FFLAGS_WIN32
) );
1012 if ( segnum
< 1 || segnum
> pModule
->ne_cseg
)
1015 if ( (pModule
->ne_flags
& NE_FFLAGS_SELFLOAD
) && segnum
!= 1 )
1016 return TRUE
; /* selfloader allocates segment itself */
1018 if ( (pSeg
->flags
& NE_SEGFLAGS_ALLOCATED
) && segnum
!= pModule
->ne_autodata
)
1019 return TRUE
; /* all but DGROUP only allocated once */
1021 minsize
= pSeg
->minsize
? pSeg
->minsize
: 0x10000;
1022 if ( segnum
== SELECTOROF(pModule
->ne_sssp
) ) minsize
+= pModule
->ne_stack
;
1023 if ( segnum
== pModule
->ne_autodata
) minsize
+= pModule
->ne_heap
;
1025 selflags
= (pSeg
->flags
& NE_SEGFLAGS_DATA
) ? WINE_LDT_FLAGS_DATA
: WINE_LDT_FLAGS_CODE
;
1026 if (pSeg
->flags
& NE_SEGFLAGS_32BIT
) selflags
|= WINE_LDT_FLAGS_32BIT
;
1027 pSeg
->hSeg
= GLOBAL_Alloc( NE_Ne2MemFlags(pSeg
->flags
), minsize
, pModule
->self
, selflags
);
1028 if (!pSeg
->hSeg
) return FALSE
;
1030 pSeg
->flags
|= NE_SEGFLAGS_ALLOCATED
;
1034 /***********************************************************************
1035 * NE_CreateAllSegments
1037 BOOL
NE_CreateAllSegments( NE_MODULE
*pModule
)
1040 for ( i
= 1; i
<= pModule
->ne_cseg
; i
++ )
1041 if ( !NE_CreateSegment( pModule
, i
) )
1044 pModule
->dgroup_entry
= pModule
->ne_autodata
? pModule
->ne_segtab
+
1045 (pModule
->ne_autodata
- 1) * sizeof(SEGTABLEENTRY
) : 0;
1050 /**********************************************************************
1051 * IsSharedSelector (KERNEL.345)
1053 BOOL16 WINAPI
IsSharedSelector16( HANDLE16 selector
)
1055 /* Check whether the selector belongs to a DLL */
1056 NE_MODULE
*pModule
= NE_GetPtr( selector
);
1057 if (!pModule
) return FALSE
;
1058 return (pModule
->ne_flags
& NE_FFLAGS_LIBMODULE
) != 0;