MIME-Version: 1.0 From: "The Owl" To: Eli Zaretskii Date: Sun, 22 Apr 2001 20:22:52 +0200 Content-type: text/plain; charset=US-ASCII Subject: Re: win2000/ntvdm/djgpp (fwd) CC: sandmann AT clio DOT rice DOT edu, djgpp-workers AT delorie DOT com In-reply-to: Reply-To: djgpp-workers AT delorie DOT com X-Mailing-List: djgpp-workers AT delorie DOT com X-Unsubscribes-To: listserv AT delorie DOT com Precedence: bulk > Thanks a lot for digging into this. In the future, please send your > messages wrt this matter to djgpp-workers AT delorie DOT com, which is a > mailing list read by DJGPP developers (Charles Sandmann does not > normally read it, so please keep him on the CC list). ok, no problem. likewise, please keep mailing me on this issue as well as i am not reading that mailing list (nor does that web archive seem to be updated since the 17th). > Just to set the record straight: does the rule run by Makefile indeed > include only the above simple commands, or do you have some more > arguments there? first of all, the first line should have read 'cmd 1| cmd2', ie. only two commands were piped, not 3 (i should not trust my memory ;-). as for the commands, they of course all take various arguments, but i do not think that matters (you will see later what i found out). > The reason I'm asking is that I'm trying to understand how exactly did > Make run those commands. Depending on the characters used by the > rule's commands, it could run the programs either via `spawn' or via > `system' library functions. If the Makefile says "SHELL = /bin/sh" or > similar, there might be a further complication because Make might > decide to run Bash and pass it the entire command line. well, the makefile is a bit complicated, you should better take a look at it yourself (http://ghiribizzo.virtualave.net/icedump/id6023.zip, then w9x/makefile around the MAKELEVEL=1 part). the makefile itself does not override SHELL however my djgpp.env does: SHELL=%DJDIR%/bin/sh.exe where sh.exe is a symlink to bash.exe. the versions of the various executables (again, i do not think it matters): GNU bash, version 2.04.7(1)-release (i386-pc-msdosdjgpp) GNU Make version 3.79.1, by Richard Stallman and Roland McGrath. Built for i386-pc-msdosdjgpp > Do you have any idea whether the fact that the interrupts are nested > have any significance here? first of all, after some more poking around in ntvdm, i am not 100% sure if they are really nested or it is just how the stack looks like when ntvdm emulates the dpmi/dos environment (the stack trace i quoted before was on the ntvdm internal stack, this is not the same as the stack that the dpmi app uses or the locked pmode stack used for reflecting certain interrupts). in any case, i think this 'nestedness' issue is irrelevant, because by the time it occurs, the stack which ntvdm wants to use for reflecting the interrupt(s) is long gone (see below). > > the last one of these interrupts seems to be some hardware irq > > reflected to this ntvdm (i did not yet debug it to see which one). > > Probably the timer, unless you touched the keyboard. Do you have some > non-standard devices on that machine which could trigger a hardware > interrupt? A network adapter, perhaps? i have a NIC, but it does not really matter what causes the interrupt, once this special stack is gone, anything that ends up being reflected will raise an exception (on my tests i observed both my mouse and even plain int 0x21). > Since this happens in nested DPMI programs, perhaps some part of NTVDM > ``remembers'' that the stack was already set up, but some other part > uncommits the memory pages because the previous image has exited? excellent guess (took me a while to debug it though ;-), see below for details. > The most useful information would be to find out where is the EIP of > the DJGPP program when this happens. the particular interrupt that ntvdm wants to reflect is initiated by a dpmi call from ___dpmi_int which in turn calls dpmi service 0x0300 (simulate real mode interrupt). > For example, I'm not even sure whether the crash happens when app4 > already runs or when Make tries to spawn it; are you now saying that > the former is the truth? i think it can be both, basically the problem can occur any time after the last app in the pipe piped app exits (because that's when the special stack gets freed). ------------------------------------------------------------------------ now, let's see the real 'meat'. i apologize for having made you read through all that above, but i did not want to leave it unanswered and jump straight into the middle of the new information i found out. 1. the actors ntvdm: this is ther user mode component of windows that does all the emulation needed by dos (v86), dosx (pm16/pm32), win16 (pm16) apps. it interfaces to the kernel via ntdll (as any other subsystem does) and also has an undocumented interface that can be used by the dos/dosx/win16 apps (based on the debug symbols, the related internal functions are prefixed by 'Bop', no idea what it stands for). dosx: this is the dos extender that sets up the protected mode environment for dosx and win16 apps. ntoskrnl: the kernel, it plays one role (that we are concerned about): it is the bridge between ntvdm and dosx in that whenever the 'Bop' interface is used by dosx, a special code sequence will raise an invalid opcode exception which ntoskrnl will recognize and will reflect the exception back to ntvdm. 2. the play i do not have a full understanding of how exactly the ntvdm subsystem works, but the following should serve as a good enough guide to understand the problem. when a djgpp app is executed, eventually an ntvdm process is created in which dosx will get executed and then the dpmi app(s), ie. we have quite a few executables loaded into a ntvdm process's address space. one of the things dosx does right at the beginning (ie. before executing the dpmi app) is to allocate 4k of memory and create an LDT selector alias and notify ntvdm that this memory range should be used as a special exception stack (whenever needed, which is the case for reflecting down hardware interrupts, etc). any memory allocation from dosx and the dpmi apps goes through ntvdm (which in turn calls ntdll/ntoskrnl). ntvdm maintains a double linked list of descriptors of all allocated memory areas. a descriptor is a simple structure, its fields describe the linear start address and size of the allocated range, the LDT alias selector created for the range and most importantly to us, the 'owner' of the given range. 'ownership' here means the PSP selector of the dpmi app that requested the memory allocation. ntvdm has an internal variable called _CurrentPSPSelector which is used to fill in the 'owner' field of this descriptor. _CurrentPSPSelector is updated whenever a new dpmi app is created inside the given ntvdm process OR when int 21/50 is called OR when a dpmi app exits (via int 21/4c normally). the reason for that ntvdm maintains this ownership information is that whenever a dpmi app exits, ntvdm will automatically free up all memory that the given app allocated. and therein lies the problem. what happens is that this special exception stack is allocated by dosx while the _CurrentPSPSelector is 0 (ie. no dpmi app has been executed yet). so far so good, after all dpmi apps will get their unique (non-zero) PSP and therefore memory allocated by them should never be confused by memory allocated by dosx. unfortunately, this is not true because when a dpmi app exits, ntvdm will not only free up all the memory allocated by the app but it will also reset _CurrentPSPSelector to 0. and should another dpmi app exit before a new one is executed, ntvdm will happily free all the memory blocks whose owner is '0' - and there goes our exception stack. now you ask, when can such a situation occur? any time a dpmi app spawns a child which spawns a grandchild (eg. 'make' executing 'sh' executing 'gcc'), when the grandchild exits, _CurrentPSPSelector is reset to 0 and if the child exits as well without spawning another grandchild, we get the problem - exception stack is gone, any interrupt thereafter that would get reflected back will cause the familiar page fault inside ntvdm. in the 'make' example, if 'make' executes two children (ie. two separate lines in the makefile), the second child will be run with damocle's sword hanging on his neck. 3. the cure the fundamental problem with ntvdm is that it maintains a simple static variable describing the 'current dpmi app' instead of having a 'stack' of PSPs of the dpmi apps as they are spawned from each other. or perhaps it could use the parent PSP field in the just exited app's PSP to update _CurrentPSPSelector (provided it is an LDT selector instead of some real mode segment). since ntvdm is beyond our control (or at least i think it is not worth the hassle to create a patch for ntvdm.exe), we can try a workaround in libc/dos/process/dosexec.c:direct_exec_tail (or whereever) where we could set the PSP (via int 21/50) ourselves back to that of the parent after the child returned (since the parent becomes again the 'current dpmi app'). so, that's all for now, i am looking for forward for the solution, the owl