Fixed a bunch of compiler warnings.
[wine.git] / loader / dos / module.c
blob5508e7c9d91fe08e398becefe064bd5cf444028e
1 /*
2 * DOS (MZ) loader
4 * Copyright 1998 Ove Kåven
6 * This code hasn't been completely cleaned up yet.
7 */
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <errno.h>
13 #include <fcntl.h>
14 #include <signal.h>
15 #include <unistd.h>
16 #include <sys/types.h>
17 #include <sys/stat.h>
18 #include "windows.h"
19 #include "winbase.h"
20 #include "module.h"
21 #include "task.h"
22 #include "selectors.h"
23 #include "file.h"
24 #include "ldt.h"
25 #include "process.h"
26 #include "miscemu.h"
27 #include "debug.h"
28 #include "dosexe.h"
29 #include "options.h"
31 #ifdef MZ_SUPPORTED
33 #include <sys/mman.h>
35 /* define this to try mapping through /proc/pid/mem instead of a temp file,
36 but Linus doesn't like mmapping /proc/pid/mem, so it doesn't work for me */
37 #undef MZ_MAPSELF
39 #define BIOS_DATA_SEGMENT 0x40
40 #define START_OFFSET 0
41 #define PSP_SIZE 0x10
43 #define SEG16(ptr,seg) ((LPVOID)((BYTE*)ptr+((DWORD)(seg)<<4)))
44 #define SEGPTR16(ptr,segptr) ((LPVOID)((BYTE*)ptr+((DWORD)SELECTOROF(segptr)<<4)+OFFSETOF(segptr)))
46 extern WORD WINAPI SYSTEM_KillSystemTimer( WORD timer );
48 static void MZ_InitPSP( LPVOID lpPSP, LPCSTR cmdline, WORD env )
50 PDB*psp=lpPSP;
51 const char*cmd=cmdline?strchr(cmdline,' '):NULL;
53 psp->int20=0x20CD; /* int 20 */
54 /* some programs use this to calculate how much memory they need */
55 psp->nextParagraph=0x9FFF;
56 psp->environment=env;
57 /* copy parameters */
58 if (cmd) {
59 #if 0
60 /* command.com doesn't do this */
61 while (*cmd == ' ') cmd++;
62 #endif
63 psp->cmdLine[0]=strlen(cmd);
64 strcpy(psp->cmdLine+1,cmd);
65 psp->cmdLine[psp->cmdLine[0]+1]='\r';
66 } else psp->cmdLine[1]='\r';
67 /* FIXME: integrate the memory stuff from Wine (msdos/dosmem.c) */
68 /* FIXME: integrate the PDB stuff from Wine (loader/task.c) */
71 static char int08[]={
72 0xCD,0x1C, /* int $0x1c */
73 0x50, /* pushw %ax */
74 0x1E, /* pushw %ds */
75 0xB8,0x40,0x00, /* movw $0x40,%ax */
76 0x8E,0xD8, /* movw %ax,%ds */
77 #if 0
78 0x83,0x06,0x6C,0x00,0x01, /* addw $1,(0x6c) */
79 0x83,0x16,0x6E,0x00,0x00, /* adcw $0,(0x6e) */
80 #else
81 0x66,0xFF,0x06,0x6C,0x00, /* incl (0x6c) */
82 #endif
83 0xB0,0x20, /* movb $0x20,%al */
84 0xE6,0x20, /* outb %al,$0x20 */
85 0x1F, /* popw %ax */
86 0x58, /* popw %ax */
87 0xCF /* iret */
90 static void MZ_InitHandlers( LPDOSTASK lpDosTask )
92 WORD seg;
93 LPBYTE start=DOSMEM_GetBlock(lpDosTask->hModule,sizeof(int08),&seg);
94 memcpy(start,int08,sizeof(int08));
95 ((SEGPTR*)(lpDosTask->img))[0x08]=PTR_SEG_OFF_TO_SEGPTR(seg,0);
98 static char enter_xms[]={
99 /* XMS hookable entry point */
100 0xEB,0x03, /* jmp entry */
101 0x90,0x90,0x90, /* nop;nop;nop */
102 /* entry: */
103 /* real entry point */
104 /* for simplicity, we'll just use the same hook as DPMI below */
105 0xCD,0x31, /* int $0x31 */
106 0xCB /* lret */
109 static void MZ_InitXMS( LPDOSTASK lpDosTask )
111 LPBYTE start=DOSMEM_GetBlock(lpDosTask->hModule,sizeof(enter_xms),&(lpDosTask->xms_seg));
112 memcpy(start,enter_xms,sizeof(enter_xms));
115 static char enter_pm[]={
116 0x50, /* pushw %ax */
117 0x52, /* pushw %dx */
118 0x55, /* pushw %bp */
119 0x89,0xE5, /* movw %sp,%bp */
120 /* get return CS */
121 0x8B,0x56,0x08, /* movw 8(%bp),%dx */
122 /* just call int 31 here to get into protected mode... */
123 /* it'll check whether it was called from dpmi_seg... */
124 0xCD,0x31, /* int $0x31 */
125 /* we are now in the context of a 16-bit relay call */
126 /* need to fixup our stack;
127 * 16-bit relay return address will be lost, but we won't worry quite yet */
128 0x8E,0xD0, /* movw %ax,%ss */
129 0x66,0x0F,0xB7,0xE5, /* movzwl %bp,%esp */
130 /* set return CS */
131 0x89,0x56,0x08, /* movw %dx,8(%bp) */
132 0x5D, /* popw %bp */
133 0x5A, /* popw %dx */
134 0x58, /* popw %ax */
135 0xCB /* lret */
138 static char wrap_rm[]={
139 0xCD,0x31, /* int $0x31 */
140 0xCB /* lret */
143 static void MZ_InitDPMI( LPDOSTASK lpDosTask )
145 unsigned size=sizeof(enter_pm)+sizeof(wrap_rm);
146 LPBYTE start=DOSMEM_GetBlock(lpDosTask->hModule,size,&(lpDosTask->dpmi_seg));
148 lpDosTask->dpmi_sel = SELECTOR_AllocBlock( start, size, SEGMENT_CODE, FALSE, FALSE );
149 lpDosTask->wrap_ofs = size-sizeof(wrap_rm);
150 lpDosTask->call_ofs = size-1;
152 memcpy(start,enter_pm,sizeof(enter_pm));
153 memcpy(start+sizeof(enter_pm),wrap_rm,sizeof(wrap_rm));
156 static WORD MZ_InitEnvironment( LPDOSTASK lpDosTask, LPCSTR env, LPCSTR name )
158 unsigned sz=0;
159 WORD seg;
160 LPSTR envblk;
162 if (env) {
163 /* get size of environment block */
164 while (env[sz++]) sz+=strlen(env+sz)+1;
165 } else sz++;
166 /* allocate it */
167 envblk=DOSMEM_GetBlock(lpDosTask->hModule,sz+sizeof(WORD)+strlen(name)+1,&seg);
168 /* fill it */
169 if (env) {
170 memcpy(envblk,env,sz);
171 } else envblk[0]=0;
172 /* DOS 3.x: the block contains 1 additional string */
173 *(WORD*)(envblk+sz)=1;
174 /* being the program name itself */
175 strcpy(envblk+sz+sizeof(WORD),name);
176 return seg;
179 int MZ_InitMemory( LPDOSTASK lpDosTask, NE_MODULE *pModule )
181 int x;
183 if (lpDosTask->img_ofs) return 32; /* already allocated */
185 /* allocate 1MB+64K shared memory */
186 lpDosTask->img_ofs=START_OFFSET;
187 #ifdef MZ_MAPSELF
188 lpDosTask->img=VirtualAlloc(NULL,0x110000,MEM_COMMIT,PAGE_READWRITE);
189 /* make sure mmap accepts it */
190 ((char*)lpDosTask->img)[0x10FFFF]=0;
191 #else
192 tmpnam(lpDosTask->mm_name);
193 /* strcpy(lpDosTask->mm_name,"/tmp/mydosimage"); */
194 lpDosTask->mm_fd=open(lpDosTask->mm_name,O_RDWR|O_CREAT /* |O_TRUNC */,S_IRUSR|S_IWUSR);
195 if (lpDosTask->mm_fd<0) ERR(module,"file %s could not be opened\n",lpDosTask->mm_name);
196 /* expand file to 1MB+64K */
197 lseek(lpDosTask->mm_fd,0x110000-1,SEEK_SET);
198 x=0; write(lpDosTask->mm_fd,&x,1);
199 /* map it in */
200 lpDosTask->img=mmap(NULL,0x110000-START_OFFSET,PROT_READ|PROT_WRITE,MAP_SHARED,lpDosTask->mm_fd,0);
201 #endif
202 if (lpDosTask->img==(LPVOID)-1) {
203 ERR(module,"could not map shared memory, error=%s\n",strerror(errno));
204 return 0;
206 TRACE(module,"DOS VM86 image mapped at %08lx\n",(DWORD)lpDosTask->img);
207 pModule->dos_image=lpDosTask->img;
209 /* initialize the memory */
210 TRACE(module,"Initializing DOS memory structures\n");
211 DOSMEM_Init(lpDosTask->hModule);
212 MZ_InitXMS(lpDosTask);
213 MZ_InitDPMI(lpDosTask);
214 return lpDosTask->hModule;
217 static int MZ_LoadImage( HFILE16 hFile, LPCSTR name, LPCSTR cmdline,
218 LPCSTR env, LPDOSTASK lpDosTask, NE_MODULE *pModule )
220 IMAGE_DOS_HEADER mz_header;
221 DWORD image_start,image_size,min_size,max_size,avail;
222 BYTE*psp_start,*load_start;
223 int x,old_com=0;
224 SEGPTR reloc;
225 WORD env_seg;
227 if ((_hread16(hFile,&mz_header,sizeof(mz_header)) != sizeof(mz_header)) ||
228 (mz_header.e_magic != IMAGE_DOS_SIGNATURE)) {
229 #if 0
230 return 11; /* invalid exe */
231 #endif
232 old_com=1; /* assume .COM file */
233 image_start=0;
234 image_size=GetFileSize(HFILE16_TO_HFILE32(hFile),NULL);
235 min_size=0x10000; max_size=0x100000;
236 mz_header.e_crlc=0;
237 mz_header.e_ss=0; mz_header.e_sp=0xFFFE;
238 mz_header.e_cs=0; mz_header.e_ip=0x100;
239 } else {
240 /* calculate load size */
241 image_start=mz_header.e_cparhdr<<4;
242 image_size=mz_header.e_cp<<9; /* pages are 512 bytes */
243 if ((mz_header.e_cblp!=0)&&(mz_header.e_cblp!=4)) image_size-=512-mz_header.e_cblp;
244 image_size-=image_start;
245 min_size=image_size+((DWORD)mz_header.e_minalloc<<4)+(PSP_SIZE<<4);
246 max_size=image_size+((DWORD)mz_header.e_maxalloc<<4)+(PSP_SIZE<<4);
249 MZ_InitMemory(lpDosTask,pModule);
251 /* allocate environment block */
252 env_seg=MZ_InitEnvironment(lpDosTask,env,name);
254 /* allocate memory for the executable */
255 TRACE(module,"Allocating DOS memory (min=%ld, max=%ld)\n",min_size,max_size);
256 avail=DOSMEM_Available(lpDosTask->hModule);
257 if (avail<min_size) {
258 ERR(module, "insufficient DOS memory\n");
259 return 0;
261 if (avail>max_size) avail=max_size;
262 psp_start=DOSMEM_GetBlock(lpDosTask->hModule,avail,&lpDosTask->psp_seg);
263 if (!psp_start) {
264 ERR(module, "error allocating DOS memory\n");
265 return 0;
267 lpDosTask->load_seg=lpDosTask->psp_seg+(old_com?0:PSP_SIZE);
268 load_start=psp_start+(PSP_SIZE<<4);
269 MZ_InitPSP(psp_start, cmdline, env_seg);
271 /* load executable image */
272 TRACE(module,"loading DOS %s image, %08lx bytes\n",old_com?"COM":"EXE",image_size);
273 _llseek16(hFile,image_start,FILE_BEGIN);
274 if ((_hread16(hFile,load_start,image_size)) != image_size)
275 return 11; /* invalid exe */
277 if (mz_header.e_crlc) {
278 /* load relocation table */
279 TRACE(module,"loading DOS EXE relocation table, %d entries\n",mz_header.e_lfarlc);
280 /* FIXME: is this too slow without read buffering? */
281 _llseek16(hFile,mz_header.e_lfarlc,FILE_BEGIN);
282 for (x=0; x<mz_header.e_crlc; x++) {
283 if (_lread16(hFile,&reloc,sizeof(reloc)) != sizeof(reloc))
284 return 11; /* invalid exe */
285 *(WORD*)SEGPTR16(load_start,reloc)+=lpDosTask->load_seg;
289 lpDosTask->init_cs=lpDosTask->load_seg+mz_header.e_cs;
290 lpDosTask->init_ip=mz_header.e_ip;
291 lpDosTask->init_ss=lpDosTask->load_seg+mz_header.e_ss;
292 lpDosTask->init_sp=mz_header.e_sp;
294 TRACE(module,"entry point: %04x:%04x\n",lpDosTask->init_cs,lpDosTask->init_ip);
296 return lpDosTask->hModule;
299 LPDOSTASK MZ_AllocDPMITask( HMODULE16 hModule )
301 LPDOSTASK lpDosTask = calloc(1, sizeof(DOSTASK));
302 NE_MODULE *pModule;
304 if (lpDosTask) {
305 lpDosTask->hModule = hModule;
307 pModule = (NE_MODULE *)GlobalLock16(hModule);
308 pModule->lpDosTask = lpDosTask;
310 lpDosTask->img=NULL; lpDosTask->mm_name[0]=0; lpDosTask->mm_fd=-1;
312 MZ_InitMemory(lpDosTask, pModule);
314 GlobalUnlock16(hModule);
316 return lpDosTask;
319 int MZ_InitTask( LPDOSTASK lpDosTask )
321 int read_fd[2],write_fd[2];
322 pid_t child;
323 char *fname,*farg,arg[16],fproc[64],path[256],*fpath;
325 if (!lpDosTask) return 0;
326 /* create read pipe */
327 if (pipe(read_fd)<0) return 0;
328 if (pipe(write_fd)<0) {
329 close(read_fd[0]); close(read_fd[1]); return 0;
331 lpDosTask->read_pipe=read_fd[0];
332 lpDosTask->write_pipe=write_fd[1];
333 /* if we have a mapping file, use it */
334 fname=lpDosTask->mm_name; farg=NULL;
335 if (!fname[0]) {
336 /* otherwise, map our own memory image */
337 sprintf(fproc,"/proc/%d/mem",getpid());
338 sprintf(arg,"%ld",(unsigned long)lpDosTask->img);
339 fname=fproc; farg=arg;
342 TRACE(module,"Loading DOS VM support module (hmodule=%04x)\n",lpDosTask->hModule);
343 if ((child=fork())<0) {
344 close(write_fd[0]); close(write_fd[1]);
345 close(read_fd[0]); close(read_fd[1]); return 0;
347 if (child!=0) {
348 /* parent process */
349 int ret;
351 close(read_fd[1]); close(write_fd[0]);
352 lpDosTask->task=child;
353 /* wait for child process to signal readiness */
354 do {
355 if (read(lpDosTask->read_pipe,&ret,sizeof(ret))!=sizeof(ret)) {
356 if ((errno==EINTR)||(errno==EAGAIN)) continue;
357 /* failure */
358 ERR(module,"dosmod has failed to initialize\n");
359 if (lpDosTask->mm_name[0]!=0) unlink(lpDosTask->mm_name);
360 return 0;
362 } while (0);
363 /* the child has now mmaped the temp file, it's now safe to unlink.
364 * do it here to avoid leaving a mess in /tmp if/when Wine crashes... */
365 if (lpDosTask->mm_name[0]!=0) unlink(lpDosTask->mm_name);
366 /* all systems are now go */
367 } else {
368 /* child process */
369 close(read_fd[0]); close(write_fd[1]);
370 /* put our pipes somewhere dosmod can find them */
371 dup2(write_fd[0],0); /* stdin */
372 dup2(read_fd[1],1); /* stdout */
373 /* now load dosmod */
374 execlp("dosmod",fname,farg,NULL);
375 execl("dosmod",fname,farg,NULL);
376 /* hmm, they didn't install properly */
377 execl("loader/dos/dosmod",fname,farg,NULL);
378 /* last resort, try to find it through argv[0] */
379 fpath=strrchr(strcpy(path,Options.argv0),'/');
380 if (fpath) {
381 strcpy(fpath,"/loader/dos/dosmod");
382 execl(path,fname,farg,NULL);
384 /* if failure, exit */
385 ERR(module,"Failed to spawn dosmod, error=%s\n",strerror(errno));
386 exit(1);
388 /* start simulated system 55Hz timer */
389 lpDosTask->system_timer = CreateSystemTimer( 55, MZ_Tick );
390 TRACE(module,"created 55Hz timer tick, handle=%d\n",lpDosTask->system_timer);
391 return lpDosTask->hModule;
394 HINSTANCE16 MZ_CreateProcess( LPCSTR name, LPCSTR cmdline, LPCSTR env,
395 LPSTARTUPINFO32A startup, LPPROCESS_INFORMATION info )
397 LPDOSTASK lpDosTask = NULL; /* keep gcc from complaining */
398 HMODULE16 hModule;
399 HINSTANCE16 hInstance;
400 PDB32 *pdb = PROCESS_Current();
401 TDB *pTask = (TDB*)GlobalLock16( GetCurrentTask() );
402 NE_MODULE *pModule = pTask ? NE_GetPtr( pTask->hModule ) : NULL;
403 HFILE16 hFile;
404 OFSTRUCT ofs;
405 int err, alloc = !(pModule && pModule->dos_image);
407 GlobalUnlock16( GetCurrentTask() );
409 if (alloc && (lpDosTask = calloc(1, sizeof(DOSTASK))) == NULL)
410 return 0;
412 if ((hFile = OpenFile16( name, &ofs, OF_READ )) == HFILE_ERROR16)
413 return 2; /* File not found */
415 if ((!env)&&pdb) env = pdb->env_db->environ;
416 if (alloc) {
417 if ((hModule = MODULE_CreateDummyModule(&ofs)) < 32)
418 return hModule;
420 lpDosTask->hModule = hModule;
422 pModule = (NE_MODULE *)GlobalLock16(hModule);
423 pModule->lpDosTask = lpDosTask;
425 lpDosTask->img=NULL; lpDosTask->mm_name[0]=0; lpDosTask->mm_fd=-1;
426 } else lpDosTask=pModule->lpDosTask;
427 err = MZ_LoadImage( hFile, name, cmdline, env, lpDosTask, pModule );
428 _lclose16(hFile);
429 if (alloc) {
430 pModule->dos_image = lpDosTask->img;
431 if (err<32) {
432 if (lpDosTask->mm_name[0]!=0) {
433 if (lpDosTask->img!=NULL) munmap(lpDosTask->img,0x110000-START_OFFSET);
434 if (lpDosTask->mm_fd>=0) close(lpDosTask->mm_fd);
435 unlink(lpDosTask->mm_name);
436 } else
437 if (lpDosTask->img!=NULL) VirtualFree(lpDosTask->img,0x110000,MEM_RELEASE);
438 return err;
440 err = MZ_InitTask( lpDosTask );
441 if (err<32) {
442 MZ_KillModule( lpDosTask );
443 /* FIXME: cleanup hModule */
444 return err;
447 hInstance = NE_CreateInstance(pModule, NULL, (cmdline == NULL));
448 PROCESS_Create( pModule, cmdline, env, hInstance, 0, startup, info );
449 return hInstance;
450 } else {
451 return (err<32) ? err : pTask->hInstance;
455 void MZ_KillModule( LPDOSTASK lpDosTask )
457 TRACE(module,"killing DOS task\n");
458 SYSTEM_KillSystemTimer(lpDosTask->system_timer);
459 if (lpDosTask->mm_name[0]!=0) {
460 munmap(lpDosTask->img,0x110000-START_OFFSET);
461 close(lpDosTask->mm_fd);
462 } else VirtualFree(lpDosTask->img,0x110000,MEM_RELEASE);
463 close(lpDosTask->read_pipe);
464 close(lpDosTask->write_pipe);
465 kill(lpDosTask->task,SIGTERM);
466 #if 0
467 /* FIXME: this seems to crash */
468 if (lpDosTask->dpmi_sel)
469 UnMapLS(PTR_SEG_OFF_TO_SEGPTR(lpDosTask->dpmi_sel,0));
470 #endif
473 #else /* !MZ_SUPPORTED */
475 HINSTANCE16 MZ_CreateProcess( LPCSTR name, LPCSTR cmdline, LPCSTR env,
476 LPSTARTUPINFO32A startup, LPPROCESS_INFORMATION info )
478 WARN(module,"DOS executables not supported on this architecture\n");
479 return (HMODULE16)11; /* invalid exe */
482 #endif