gdi32: Fix the sign of the vertical text position in ExtTextOutW.
[wine/multimedia.git] / dlls / krnl386.exe16 / ne_segment.c
blob48910e4013c73b75e802b52d319fedf85a486f04
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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 "winternl.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
70 * Relocation types
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 */
79 typedef struct
81 WORD version; /* Must be "A0" (0x3041) */
82 WORD reserved;
83 FARPROC16 BootApp; /* startup procedure */
84 FARPROC16 LoadAppSeg; /* procedure to load a segment */
85 FARPROC16 reserved2;
86 FARPROC16 MyAlloc; /* memory allocation procedure,
87 * wine must write this field */
88 FARPROC16 EntryAddrProc;
89 FARPROC16 ExitProc; /* exit procedure */
90 WORD reserved3[4];
91 FARPROC16 SetOwner; /* Set Owner procedure, exported by wine */
92 } SELFLOADHEADER;
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";
113 return "???";
117 /***********************************************************************
118 * NE_OpenFile
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 );
129 return HFILE_ERROR;
131 return Win32HandleToDosFileHandle( handle );
135 /***********************************************************************
136 * apply_relocations
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 )
143 BYTE *func_name;
144 char buffer[256];
145 int i, ordinal;
146 WORD offset, *sp;
147 HMODULE16 module;
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
163 and target */
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 );
171 if (!address)
173 NE_MODULE *pTarget = NE_GetPtr( module );
174 if (!pTarget)
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 );
180 else
182 ERR("No implementation for %.*s.%d, setting to 0xdeadbeef\n",
183 *((BYTE *)pTarget + pTarget->ne_restab),
184 (char *)pTarget + pTarget->ne_restab + 1,
185 ordinal );
186 address = (FARPROC16)0xdeadbeef;
189 if (TRACE_ON(fixup))
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 ) );
198 break;
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;
216 if (TRACE_ON(fixup))
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 ) );
225 break;
227 case NE_RELTYPE_INTERNAL:
228 if ((rep->target1 & 0xff) == 0xff)
230 address = NE_GetEntryPoint( pModule->self, rep->target2 );
232 else
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 ) );
240 break;
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
249 * exist.
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 ) );
255 continue;
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)
264 char module[10];
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 );
270 if (additive)
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);
278 break;
279 case NE_RADDR_OFFSET16:
280 *sp += LOWORD(address);
281 break;
282 case NE_RADDR_POINTER32:
283 *sp += LOWORD(address);
284 *(sp+1) = HIWORD(address);
285 break;
286 case NE_RADDR_SELECTOR:
287 /* Borland creates additive records with offset zero. Strange, but OK */
288 if (*sp)
289 ERR("Additive selector to %04x.Please report\n",*sp);
290 else
291 *sp = HIWORD(address);
292 break;
293 default:
294 goto unknown;
297 else /* non-additive fixup */
301 WORD next_offset;
303 sp = MapSL( MAKESEGPTR( SEL(pSeg->hSeg), offset ) );
304 next_offset = *sp;
305 TRACE(" %04x:%04x\n", offset, *sp );
306 switch (rep->address_type & 0x7f)
308 case NE_RADDR_LOWBYTE:
309 *(BYTE *)sp = LOBYTE((int)address);
310 break;
311 case NE_RADDR_OFFSET16:
312 *sp = LOWORD(address);
313 break;
314 case NE_RADDR_POINTER32:
315 *(FARPROC16 *)sp = address;
316 break;
317 case NE_RADDR_SELECTOR:
318 *sp = SELECTOROF(address);
319 break;
320 default:
321 goto unknown;
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);
329 return TRUE;
331 unknown:
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);
336 return FALSE;
340 /***********************************************************************
341 * NE_LoadSegment
343 BOOL NE_LoadSegment( NE_MODULE *pModule, WORD segnum )
345 WORD count;
346 DWORD pos;
347 const struct relocation_entry_s *rep;
348 int size;
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)
356 return TRUE;
358 /* leave, except for DGROUP, as this may be the second instance */
359 if (segnum != pModule->ne_autodata)
360 return TRUE;
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;
375 void *oldstack;
376 HFILE16 hFile16;
377 WORD args[3];
378 DWORD ret;
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;
389 args[1] = hFile16;
390 args[0] = segnum;
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;
398 return TRUE;
400 else if (!(pSeg->flags & NE_SEGFLAGS_ITERATED))
402 void *mem = GlobalLock16(pSeg->hSeg);
403 if (!NE_READ_DATA( pModule, mem, pos, size ))
404 return FALSE;
405 pos += size;
407 else
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);
418 pos += size;
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);
426 while (rept--)
428 memcpy( mem, curr, len );
429 mem += len;
431 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) )))
452 return FALSE;
454 return apply_relocations( pModule, rep, count, segnum );
458 /***********************************************************************
459 * NE_LoadAllSegments
461 BOOL NE_LoadAllSegments( NE_MODULE *pModule )
463 int i;
464 SEGTABLEENTRY * pSegTable = NE_SEG_TABLE(pModule);
466 if (pModule->ne_flags & NE_FFLAGS_SELFLOAD)
468 HFILE16 hFile16;
469 HGLOBAL16 sel;
470 /* Handle self-loading modules */
471 SELFLOADHEADER *selfloadheader;
472 HMODULE16 mod = GetModuleHandle16("KERNEL");
473 void *oldstack;
474 WORD args[2];
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;
495 args[0] = hFile16;
496 WOWCallback16Ex( (DWORD)selfloadheader->BootApp, WCB16_PASCAL, sizeof(args), args, NULL );
497 TRACE_(dll)("Return from CallBootAppProc\n");
498 _lclose16(hFile16);
499 NtCurrentTeb()->WOW32Reserved = oldstack;
501 for (i = 2; i <= pModule->ne_cseg; i++)
502 if (!NE_LoadSegment( pModule, i )) return FALSE;
504 else
506 for (i = 1; i <= pModule->ne_cseg; i++)
507 if (!NE_LoadSegment( pModule, i )) return FALSE;
509 return TRUE;
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 );
521 ET_BUNDLE *bundle;
522 ET_ENTRY *entry;
523 WORD dgroup, num_entries, sel = SEL(pSegTable[segnum-1].hSeg);
524 BYTE *pSeg, *pFunc;
526 TRACE("(%d);\n", segnum);
528 if (pSegTable[segnum-1].flags & NE_SEGFLAGS_DATA)
530 pSegTable[segnum-1].flags |= NE_SEGFLAGS_LOADED;
531 return;
534 if (!pModule->ne_autodata) return;
536 if (!(dgroup = SEL(pSegTable[pModule->ne_autodata-1].hSeg))) return;
538 pSeg = MapSL( MAKESEGPTR(sel, 0) );
540 bundle = (ET_BUNDLE *)((BYTE *)pModule+pModule->ne_enttab);
542 do {
543 TRACE("num_entries: %d, bundle: %p, next: %04x, pSeg: %p\n", bundle->last - bundle->first, bundle, bundle->next, pSeg);
544 if (!(num_entries = bundle->last - bundle->first))
545 return;
546 entry = (ET_ENTRY *)((BYTE *)bundle+6);
547 while (num_entries--)
549 /*TRACE("entry: %p, entry->segnum: %d, entry->offs: %04x\n", entry, entry->segnum, entry->offs);*/
550 if (entry->segnum == segnum)
552 pFunc = pSeg+entry->offs;
553 TRACE("pFunc: %p, *(DWORD *)pFunc: %08x, num_entries: %d\n", pFunc, *(DWORD *)pFunc, num_entries);
554 if (*(pFunc+2) == 0x90)
556 if (*(WORD *)pFunc == 0x581e) /* push ds, pop ax */
558 TRACE("patch %04x:%04x -> mov ax, ds\n", sel, entry->offs);
559 *(WORD *)pFunc = 0xd88c; /* mov ax, ds */
562 if (*(WORD *)pFunc == 0xd88c)
564 if ((entry->flags & 2)) /* public data ? */
566 TRACE("patch %04x:%04x -> mov ax, dgroup [%04x]\n", sel, entry->offs, dgroup);
567 *pFunc = 0xb8; /* mov ax, */
568 *(WORD *)(pFunc+1) = dgroup;
570 else if ((pModule->ne_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) && (bundle = ((ET_BUNDLE *)((BYTE *)pModule + bundle->next))) );
585 /***********************************************************************
586 * PatchCodeHandle (KERNEL.110)
588 * Needed for self-loading modules.
590 DWORD WINAPI PatchCodeHandle16(HANDLE16 hSeg)
592 WORD segnum;
593 WORD sel = SEL(hSeg);
594 NE_MODULE *pModule = NE_GetPtr(FarGetOwner16(sel));
595 SEGTABLEENTRY *pSegTable = NE_SEG_TABLE(pModule);
597 TRACE_(module)("(%04x);\n", hSeg);
599 /* find the segment number of the module that belongs to hSeg */
600 for (segnum = 1; segnum <= pModule->ne_cseg; segnum++)
602 if (SEL(pSegTable[segnum-1].hSeg) == sel)
604 NE_FixupSegmentPrologs(pModule, segnum);
605 break;
609 return MAKELONG(hSeg, sel);
613 /***********************************************************************
614 * NE_GetDLLInitParams
616 static VOID NE_GetDLLInitParams( NE_MODULE *pModule,
617 WORD *hInst, WORD *ds, WORD *heap )
619 SEGTABLEENTRY *pSegTable = NE_SEG_TABLE( pModule );
621 if (!(pModule->ne_flags & NE_FFLAGS_SINGLEDATA))
623 if (pModule->ne_flags & NE_FFLAGS_MULTIPLEDATA || pModule->ne_autodata)
625 /* Not SINGLEDATA */
626 ERR_(dll)("Library is not marked SINGLEDATA\n");
627 exit(1);
629 else /* DATA NONE DLL */
631 *ds = 0;
632 *heap = 0;
635 else /* DATA SINGLE DLL */
637 if (pModule->ne_autodata) {
638 *ds = SEL(pSegTable[pModule->ne_autodata-1].hSeg);
639 *heap = pModule->ne_heap;
641 else /* hmm, DLL has no dgroup,
642 but why has it NE_FFLAGS_SINGLEDATA set ?
643 Buggy DLL compiler ? */
645 *ds = 0;
646 *heap = 0;
650 *hInst = *ds ? GlobalHandle16(*ds) : pModule->self;
654 /***********************************************************************
655 * NE_InitDLL
657 * Call the DLL initialization code
659 static BOOL NE_InitDLL( NE_MODULE *pModule )
661 SEGTABLEENTRY *pSegTable;
662 WORD hInst, ds, heap;
663 CONTEXT86 context;
665 pSegTable = NE_SEG_TABLE( pModule );
667 if (!(pModule->ne_flags & NE_FFLAGS_LIBMODULE) ||
668 (pModule->ne_flags & NE_FFLAGS_WIN32)) return TRUE; /*not a library*/
670 /* Call USER signal handler for Win3.1 compatibility. */
671 NE_CallUserSignalProc( pModule->self, USIG16_DLL_LOAD );
673 if (!SELECTOROF(pModule->ne_csip)) return TRUE; /* no initialization code */
676 /* Registers at initialization must be:
677 * cx heap size
678 * di library instance
679 * ds data segment if any
680 * es:si command line (always 0)
683 memset( &context, 0, sizeof(context) );
685 NE_GetDLLInitParams( pModule, &hInst, &ds, &heap );
687 context.Ecx = heap;
688 context.Edi = hInst;
689 context.SegDs = ds;
690 context.SegEs = ds; /* who knows ... */
691 context.SegFs = wine_get_fs();
692 context.SegGs = wine_get_gs();
693 context.SegCs = SEL(pSegTable[SELECTOROF(pModule->ne_csip)-1].hSeg);
694 context.Eip = OFFSETOF(pModule->ne_csip);
695 context.Ebp = OFFSETOF(NtCurrentTeb()->WOW32Reserved) + FIELD_OFFSET(STACK16FRAME,bp);
697 pModule->ne_csip = 0; /* Don't initialize it twice */
698 TRACE_(dll)("Calling LibMain for %.*s, cs:ip=%04x:%04x ds=%04x di=%04x cx=%04x\n",
699 *((BYTE*)pModule + pModule->ne_restab),
700 (char *)pModule + pModule->ne_restab + 1,
701 context.SegCs, context.Eip, context.SegDs,
702 LOWORD(context.Edi), LOWORD(context.Ecx) );
703 WOWCallback16Ex( 0, WCB16_REGS, 0, NULL, (DWORD *)&context );
704 return TRUE;
707 /***********************************************************************
708 * NE_InitializeDLLs
710 * Recursively initialize all DLLs (according to the order in which
711 * they where loaded).
713 void NE_InitializeDLLs( HMODULE16 hModule )
715 NE_MODULE *pModule;
716 HMODULE16 *pDLL;
718 if (!(pModule = NE_GetPtr( hModule ))) return;
719 assert( !(pModule->ne_flags & NE_FFLAGS_WIN32) );
721 if (pModule->dlls_to_init)
723 HGLOBAL16 to_init = pModule->dlls_to_init;
724 pModule->dlls_to_init = 0;
725 for (pDLL = GlobalLock16( to_init ); *pDLL; pDLL++)
727 NE_InitializeDLLs( *pDLL );
729 GlobalFree16( to_init );
731 NE_InitDLL( pModule );
735 /**********************************************************************
736 * NE_CallUserSignalProc
738 * According to "Undocumented Windows", the task signal proc is
739 * bypassed for module load/unload notifications, and the USER signal
740 * proc is called directly instead. This is what this function does.
742 typedef DWORD (WINAPI *pSignalProc)( HANDLE16 module, UINT16 code, UINT16 exit,
743 HINSTANCE16 inst, HQUEUE16 queue );
745 void NE_CallUserSignalProc( HMODULE16 hModule, UINT16 code )
747 FARPROC16 proc;
748 HMODULE16 user = GetModuleHandle16("user.exe");
750 if (!user) return;
751 if ((proc = GetProcAddress16( user, "SignalProc" )))
753 /* USER is always a builtin dll */
754 pSignalProc sigproc = (pSignalProc)((ENTRYPOINT16 *)MapSL( (SEGPTR)proc ))->target;
755 sigproc( hModule, code, 0, 0, 0 );
760 /***********************************************************************
761 * NE_CallDllEntryPoint
763 * Call the DllEntryPoint of DLLs with subsystem >= 4.0
765 typedef DWORD (WINAPI *WinNEEntryProc)(DWORD,WORD,WORD,WORD,DWORD,WORD);
767 static void NE_CallDllEntryPoint( NE_MODULE *pModule, DWORD dwReason )
769 WORD hInst, ds, heap;
770 FARPROC16 entryPoint;
772 if (!(pModule->ne_flags & NE_FFLAGS_LIBMODULE)) return;
773 if (!(pModule->ne_flags & NE_FFLAGS_BUILTIN) && pModule->ne_expver < 0x0400) return;
774 if (!(entryPoint = GetProcAddress16( pModule->self, "DllEntryPoint" ))) return;
776 NE_GetDLLInitParams( pModule, &hInst, &ds, &heap );
778 TRACE_(dll)( "Calling %s DllEntryPoint, cs:ip=%04x:%04x\n",
779 NE_MODULE_NAME( pModule ),
780 SELECTOROF(entryPoint), OFFSETOF(entryPoint) );
782 if ( pModule->ne_flags & NE_FFLAGS_BUILTIN )
784 WinNEEntryProc entryProc = (WinNEEntryProc)((ENTRYPOINT16 *)MapSL( (SEGPTR)entryPoint ))->target;
786 entryProc( dwReason, hInst, ds, heap, 0, 0 );
788 else
790 CONTEXT86 context;
791 WORD args[8];
793 memset( &context, 0, sizeof(context) );
794 context.SegDs = ds;
795 context.SegEs = ds; /* who knows ... */
796 context.SegFs = wine_get_fs();
797 context.SegGs = wine_get_gs();
798 context.SegCs = HIWORD(entryPoint);
799 context.Eip = LOWORD(entryPoint);
800 context.Ebp = OFFSETOF(NtCurrentTeb()->WOW32Reserved) + FIELD_OFFSET(STACK16FRAME,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->ne_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->ne_flagsothers & 0x80 ) return;
895 TRACE_(dll)("(%s) - START\n", NE_MODULE_NAME(pModule) );
897 /* Tag current module to prevent recursive loop */
898 pModule->ne_flagsothers |= 0x80;
900 /* Recursively attach all DLLs this one depends on */
901 pModRef = (HMODULE16 *)((char *)pModule + pModule->ne_modtab);
902 for ( i = 0; i < pModule->ne_cmod; 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->ne_flagsothers &= ~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->ne_autodata )
991 return pModule->self;
992 else
994 SEGTABLEENTRY *pSeg;
995 pSeg = NE_SEG_TABLE( pModule ) + pModule->ne_autodata - 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->ne_flags & NE_FFLAGS_WIN32) );
1011 if ( segnum < 1 || segnum > pModule->ne_cseg )
1012 return FALSE;
1014 if ( (pModule->ne_flags & NE_FFLAGS_SELFLOAD) && segnum != 1 )
1015 return TRUE; /* selfloader allocates segment itself */
1017 if ( (pSeg->flags & NE_SEGFLAGS_ALLOCATED) && segnum != pModule->ne_autodata )
1018 return TRUE; /* all but DGROUP only allocated once */
1020 minsize = pSeg->minsize ? pSeg->minsize : 0x10000;
1021 if ( segnum == SELECTOROF(pModule->ne_sssp) ) minsize += pModule->ne_stack;
1022 if ( segnum == pModule->ne_autodata ) minsize += pModule->ne_heap;
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->ne_cseg; i++ )
1040 if ( !NE_CreateSegment( pModule, i ) )
1041 return FALSE;
1043 pModule->dgroup_entry = pModule->ne_autodata ? pModule->ne_segtab +
1044 (pModule->ne_autodata - 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->ne_flags & NE_FFLAGS_LIBMODULE) != 0;