Date: Sat, 12 May 2001 12:48:13 +0300 From: "Eli Zaretskii" Sender: halo1 AT zahav DOT net DOT il To: theowl AT freemail DOT c3 DOT hu Message-Id: <4331-Sat12May2001124812+0300-eliz@is.elta.co.il> X-Mailer: Emacs 20.6 (via feedmail 8.3.emacs20_6 I) and Blat ver 1.8.9 CC: djgpp-workers AT delorie DOT com, sandmann AT clio DOT rice DOT edu In-reply-to: <3AF29483.25527.47F6564@localhost> (theowl@freemail.c3.hu) Subject: Re: win2000/ntvdm/djgpp (fwd) References: <3AF1D04F DOT 23054 DOT 180FCC1 AT localhost> (theowl AT freemail DOT c3 DOT hu) <3AF29483 DOT 25527 DOT 47F6564 AT localhost> Reply-To: djgpp-workers AT delorie DOT com Errors-To: nobody AT delorie DOT com X-Mailing-List: djgpp-workers AT delorie DOT com X-Unsubscribes-To: listserv AT delorie DOT com Precedence: bulk Sorry for such a long delay. I finally found enough time to write and test this. Here are patches to two library modules and one header. They should apply successfully to both the stock v2.03 sources and to the current CVS, so you can use any of them. These changes implement the method of issuing a PM Int 21h/AX=50h to set the PSP selector after a child program exits, and also when a DJGPP program exits (in case the child and the parent were interrupted by Ctrl-C, or the child wasn't a DJGPP program). Since this code is primarily meant for programs which routinely run other programs, I specifically tried to cache the heck out of it, making it as fast as possible (let's hope W2K doesn't change its version while we run ;-). Please eyeball the code for potential blunders and/or more optimization opportunities. I didn't yet try to write up the alternative method, with AX=51h. Let's see if this one works first. Docs changes are also not yet there, for the same reason. Tested on plain DOS 5.0 and on Windows 98 by building djlsr with a Make built with the patched library. In addition to testing on W2K, it would be nice if someone could test this on NT4, since the new code will spring into action there as well (in contrast to other systems, where it is almost a no-op). Plain DOS versions 6.x and FreeDOS (with its own DPMI host) are also good candidates for testing. It is advisable to rebuild as many DJGPP tools as possible with a modified libc, before you test; GCC, Bash, Make, and Fileutils seem like the best candidates. Then build some large packages with the patched tools, and see if you spot anything unusual. TIA --- src/libc/go32/dpmiexcp.c~0 Thu Sep 2 04:30:38 1999 +++ src/libc/go32/dpmiexcp.c Sat May 12 10:40:56 2001 @@ -21,6 +21,7 @@ #include #include /* For DS base/limit info */ #include +#include #define err(x) _write(STDERR_FILENO, x, sizeof(x)-1) @@ -585,9 +586,87 @@ __djgpp_set_ctrl_c(int enable_sigs) return oldenable; } +unsigned short _windows_major, _windows_minor; + +/* Compute the version Windows reports via Int 2Fh/AX=1600h. */ +static void +get_windows_version(void) +{ + if (!_windows_major) + { + __dpmi_regs r; + + r.x.ax = 0x1600; + __dpmi_int(0x2f, &r); + if (r.h.al > 2 && r.h.al != 0x80 && r.h.al != 0xff + && (r.h.al > 3 || r.h.ah > 0)) + { + _windows_major = r.h.al; + _windows_minor = r.h.ah; + } + else + _windows_major = 0xff; /* meaning no Windows */ + } +} + +/* A work-around for a bug in W2K's NTVDM, suggested by + , who wishes to remain anonymous. + + When a DPMI program exits, NTVDM resets its notion of the current + program's PSP selector to 0. If that DPMI program was spawned by + another DPMI program, and the parent program exits while the PSP + selector recorded by NTVDM is still 0, NTVDM will think that DOSX, + the NT DOS extender, is itself exiting, i.e. that the DOS box is + being closed. It will then free up all the memory blocks owned by + DOSX, and that memory includes the locked PM stack used for + processing hardware interrupts reflected into protected mode. + Thereafter, any hardware interrupt (a timer tick, a keyboard + keypress, anything) will crash NTVDM because NTVDM will try to use + a stack which was freed. + + The bug which causes this is that NTVDM resets the PSP to 0, + instead of setting it to the PSP of the parent program. + + To work around that, we force NTVDM to record a valid PSP before we + exit. We do that by invoking a PM Int 21h, function 50h, passing + it our PM selector for the PSP, as it was returned by the PM switch + entry point we called at startup to enter protected mode. We do + that just before exiting, to make sure that even an application + which crashes (e.g., due to SIGSEGV or Ctrl-BREAK) immediately + after its child returns will always leave its valid PSP recorded by + NTVDM before it exits. + + (To play it safe in the face of non-DJGPP DPMI programs and old + DJGPP programs, we also restore the PSP in dosexec.c, which see.) + + Note that we invoke here a PM Int 21, passing it the PM selector of + our PSP. This is _not_ a call to __dpmi_int with a real-mode PSP + address! */ +void +__maybe_fix_w2k_ntvdm_bug(void) +{ + /* The _osmajor == 0 case takes care of a crash that happens before + we had a chance to get DOS version. (Yes, I am being paranoiac!) */ + if ((_osmajor == 5 || _osmajor == 0) && _osminor == 0 + && _get_dos_version(1) == 0x0532) /* NT or Windows 2000? */ + { + get_windows_version(); + if (_windows_major == 0xff) /* this is NT or W2K */ + { + extern int _int86(int ivec, union REGS *in, union REGS *out); + union REGS regs; + + regs.h.ah = 0x50; + regs.d.ebx = _stubinfo->psp_selector; + _int86 (0x21, ®s, ®s); + } + } +} + void __attribute__((noreturn)) _exit(int status) { + __maybe_fix_w2k_ntvdm_bug(); /* If we are exiting due to an FP exception, the next program run in the same DOS box on Windows crashes during startup. Clearing the 80x87 seems to prevent this, at least in some cases. We only do that if a --- src/libc/dos/process/dosexec.c~2 Sun Oct 1 12:32:08 2000 +++ src/libc/dos/process/dosexec.c Sat May 12 10:32:06 2001 @@ -382,6 +382,9 @@ direct_exec_tail(const char *program, co __dpmi_free_dos_memory (tbuf_selector); tbuf_selector = 0; #endif + /* Work around the W2K NTVDM bug; see dpmiexcp.c for detailed + explanations. */ + __maybe_fix_w2k_ntvdm_bug(); if (r.x.flags & 1) { errno = __doserr_to_errno(r.x.ax); --- include/dos.h~0 Thu Jun 3 13:22:28 1999 +++ include/dos.h Sat May 12 10:30:02 2001 @@ -130,6 +130,7 @@ #endif extern unsigned short _osmajor, _osminor; +extern unsigned short _windows_major, _windows_minor; extern const char * _os_flavor; extern int _doserrno; @@ -167,6 +168,8 @@ /* int _get_default_drive(void); void _fixpath(const char *, char *); */ +void __maybe_fix_w2k_ntvdm_bug(void); + /* * For compatibility with other DOS C compilers.