* go32-nat.c: Add comments about dirty secrets of DJGPP debugging.

This commit is contained in:
Eli Zaretskii 2009-05-01 17:45:43 +00:00
parent 1f7f5cbe1c
commit 699275c920
2 changed files with 68 additions and 0 deletions

View File

@ -1,3 +1,7 @@
2009-05-01 Eli Zaretskii <eliz@gnu.org>
* go32-nat.c: Add comments about dirty secrets of DJGPP debugging.
2009-05-01 Jan Kratochvil <jan.kratochvil@redhat.com>
Make specifiable the make_function_type type memory ownership.

View File

@ -18,6 +18,70 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* To whomever it may concern, here's a general description of how
debugging in DJGPP works, and the special quirks GDB does to
support that.
When the DJGPP port of GDB is debugging a DJGPP program natively,
there aren't 2 separate processes, the debuggee and GDB itself, as
on other systems. (This is DOS, where there can only be one active
process at any given time, remember?) Instead, GDB and the
debuggee live in the same process. So when GDB calls
go32_create_inferior below, and that function calls edi_init from
the DJGPP debug support library libdbg.a, we load the debuggee's
executable file into GDB's address space, set it up for execution
as the stub loader (a short real-mode program prepended to each
DJGPP executable) normally would, and do a lot of preparations for
swapping between GDB's and debuggee's internal state, primarily wrt
the exception handlers. This swapping happens every time we resume
the debuggee or switch back to GDB's code, and it includes:
. swapping all the segment registers
. swapping the PSP (the Program Segment Prefix)
. swapping the signal handlers
. swapping the exception handlers
. swapping the FPU status
. swapping the 3 standard file handles (more about this below)
Then running the debuggee simply means longjmp into it where its PC
is and let it run until it stops for some reason. When it stops,
GDB catches the exception that stopped it and longjmp's back into
its own code. All the possible exit points of the debuggee are
watched; for example, the normal exit point is recognized because a
DOS program issues a special system call to exit. If one of those
exit points is hit, we mourn the inferior and clean up after it.
Cleaning up is very important, even if the process exits normally,
because otherwise we might leave behind traces of previous
execution, and in several cases GDB itself might be left hosed,
because all the exception handlers were not restored.
Swapping of the standard handles (in redir_to_child and
redir_to_debugger) is needed because, since both GDB and the
debuggee live in the same process, as far as the OS is concerned,
the share the same file table. This means that the standard
handles 0, 1, and 2 point to the same file table entries, and thus
are connected to the same devices. Therefore, if the debugger
redirects its standard output, the standard output of the debuggee
is also automagically redirected to the same file/device!
Similarly, if the debuggee redirects its stdout to a file, you
won't be able to see debugger's output (it will go to the same file
where the debuggee has its output); and if the debuggee closes its
standard input, you will lose the ability to talk to debugger!
For this reason, every time the debuggee is about to be resumed, we
call redir_to_child, which redirects the standard handles to where
the debuggee expects them to be. When the debuggee stops and GDB
regains control, we call redir_to_debugger, which redirects those 3
handles back to where GDB expects.
Note that only the first 3 handles are swapped, so if the debuggee
redirects or closes any other handles, GDB will not notice. In
particular, the exit code of a DJGPP program forcibly closes all
file handles beyond the first 3 ones, so when the debuggee exits,
GDB currently loses its stdaux and stdprn streams. Fortunately,
GDB does not use those as of this writing, and will never need
to. */
#include <fcntl.h>
#include "defs.h"