Mail Archives: djgpp-workers/2001/05/12/06:20:31
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 <sys/exceptn.h>
#include <sys/nearptr.h> /* For DS base/limit info */
#include <libc/internal.h>
+#include <stubinfo.h>
#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
+ <theowl AT freemail DOT c3 DOT h>, 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.
- Raw text -