delorie.com/archives/browse.cgi   search  
Mail Archives: djgpp-workers/2001/03/22/01:15:02

Message-Id: <3.0.1.32.20010322141215.00706d80@wingate>
X-Sender: n_abing#ns DOT roxas-online DOT net DOT ph AT wingate
X-Mailer: Windows Eudora Pro Version 3.0.1 (32)
Date: Thu, 22 Mar 2001 14:12:15 +0800
To: djgpp-workers AT delorie DOT com
From: "Nimrod A. Abing" <n_abing AT ns DOT roxas-online DOT net DOT ph>
Subject: Core dumping for DJGPP programs
Cc: sandmann AT clio DOT rice DOT edu
Mime-Version: 1.0
Reply-To: djgpp-workers AT delorie DOT com

Hello.

I've recently begun work on a core dumping facility for DJGPP. This would
allow DJGPP users to do post-mortem debugging of their programs. This is
particularly useful for debugging programs that use Allegro or other
graphics-based programs with GDB.

I've been discussing this with Eli and, with his guidance, I've been
working on it for about three weeks now. He also pointed out an old post by
George Foot about post-mortem debugging on the DJGPP mailing-list archives.
George had already written the core dumping facility along with a primitive
(yet functional) command-line debugger. However, his work was a bit
incomplete. He was kind enough to provide me with his sources and let me
work on improving it.

I did not attach those sources (with some changes I made) because the 26K
zip file might annoy some of you here, but if you want I can send it to you
in a separate post.

Anyway, here's what GF's code does:

It is a module that you link with your program, assuming you also compiled
it with debugging info. In your program, you call a function that will hook
your specified signal with the core dumper. The core dumper properly chains
to an existing signal handler if you want it to. For example, you can hook
SIGSEGV to the core dumper and tell it to chain to the old handler. If the
old handler is __djgpp_traceback_exit(), it will call that handler after
the core is dumped. This allows one to see a traceback screen, as usual,
and get a core dump as well.

As for the core file and the way it's laid out, here's an excerpt from GF's
Readme.txt:

	Core files start with a six-byte ID string (`pmdbcd', for
	"post mortem debugger core dump").  This is followed by a 
	two-byte version number (major, minor).
	
	After that header the file is divided into sections.  Each
	has a four-character ID string, followed by a 32-bit size 
	value, then that number of data bytes.
	
	All multi-byte numbers are little-endian.
	
	The sections currently used are:
	
		file: Stores the filename of the executable (from 
			argv[0]).  It's not NULL-terminated.
		
		sgnl: Information about the signal that was raised.
			Three 32-bit numbers.  The first is the signal
			as passed to the handler.  The second is the 
			__signum field of the exception state variable; 
			this gives more information about the error in
			some cases.  The third is the __sigmask field; 
			I'm not sure what it's for.
		
		regs: Register contents -- 32 bits each for eax, ebx, 
			ecx, edx, esi, edi, ebp, esp.  I'll add some 
			others too sometime.
		
		sels: Selector details -- handles, base addresses and 
			limits for cs, ds, es, fs, gs and ss.  All 32-bit,
			stored in that order (i.e. cs handle, cs address,
			cs limit, ds handle, ...).
		
		cftb: Call frame traceback.  This is just a list of 
			32-bit offsets.
		
		blks: DPMI memory block information.  For each memory
			block, the base address, size (guessed) and chunk 
			count are stored as 32-bit numbers.  The chunk 
			count is the number of 64K chunks of this block
			which were dumped to the `data' section.
		
		data: Memory dump.  This is just a big block of data.
			It holds the first DPMI memory block's chunks 
			first, then the second's, etc.
		
		end.: This has zero data size, and just marks the end 
			of the file.

What's missing in the regs section is the value of eip. I've looked into
dpmiexcp.c to see how __djgpp_traceback_exit() does this, and this is what
I've written:

  if (__djgpp_exception_state == fake_exception
      && __djgpp_exception_state -> __cs == _my_cs()
      && (unsigned)__djgpp_exception_state -> __ebp
      >= __djgpp_exception_state -> __esp
      && (unsigned *)__djgpp_exception_state -> __ebp >= &end
      && (unsigned *)__djgpp_exception_state -> __ebp
      < (unsigned *)__djgpp_selector_limit)
    _writei4 (corefile, *((unsigned *)__djgpp_exception_state -> __ebp + 1));
  else
    _writei4 (corefile, __djgpp_exception_state -> __eip);

which is basically what the code in dpmiexcp does, except the value of eip
is written to an open file. The eip is correctly written for signals other
than the fake signal SIGABRT (well for SIGSEGV anyway, eip is correctly
written). I also noticed discrepancies in the values of the registers eax,
ecx, edx, esi, edi, ebp, and esp. There's a big difference between the ones
generated by the traceback and the ones stored in the core file, this
apparently happens for fake signal SIGABRT, doesn't seem to happen for
SIGSEGV.

This is one of the test program's results:

DJGPP traceback
----- cut here -----
Exiting due to signal SIGABRT
Raised at eip=00004e96
eax=00090224 ebx=00000120 ecx=0000047c edx=0000d568 esi=00000007 edi=00000018
ebp=000902d0 esp=00090220 program=C:\STUFF\PROJECTS\PMDB\COREDUMP\ABRT.EXE
cs: sel=00f7  base=83744000  limit=0009ffff
ds: sel=00ff  base=83744000  limit=0009ffff
es: sel=00ff  base=83744000  limit=0009ffff
fs: sel=00d7  base=0000de20  limit=0000ffff
gs: sel=010f  base=00000000  limit=0010ffff
ss: sel=00ff  base=83744000  limit=0009ffff
App stack: [0009046c..0001046c]  Exceptn stack: [000103cc..0000e48c]

Call frame traceback EIPs:
  0x00004db4
  0x00004e96
  0x00002756
  0x00004e9b
  0x0000410b
  0x0000157b
  0x0000158f
  0x0000159f
  0x000015c1
  0x00003d02
----- cut here -----

This is the output from pmdb (GF's pm debugger), given the `i' command:

----- cut here -----

reading exe file `C:\STUFF\PROJECTS\PMDB\COREDUMP\ABRT.EXE'...

For help use the `h'elp command.
pmdb command: i

Signal:
@ eip   = 4e91
  sig   = 0120    SIGABRT
  num   = 007b
  error = 00ff

Registers:
  eax: 00090334   ebx: 00000120   ecx: 00000000   edx: 0000de20
  esi: 00000054   edi: 0001046c   ebp: 000903e0   esp: 000902e8

Selectors:
  cs: 00f7  (base = 83744000, limit = 0009ffff)
  ds: 00ff  (base = 83744000, limit = 0009ffff)
  es: 00ff  (base = 83744000, limit = 0009ffff)
  fs: 00d7  (base = 0000de20, limit = 0000ffff)
  gs: 010f  (base = 00000000, limit = 0010ffff)
  ss: 00ff  (base = 83744000, limit = 0009ffff)

pmdb command:
----- cut here -----

In it's current state, the code works fine for SIGSEGV and SIGFPE. The
values of the regs are correct. The core file now also includes the
environment variables inherited by the running program. I just dump the
``invisible'' DJGPP global `environ'.

Again, if you need the sources, I'll be more than happy to send them to you.

Thanks for reading this.


nimrod_a_abing
--------------

+========================================+
|  Home page: www.geocities.com/n_abing  |
+========================================+

"Tinimbang ka ngunit kulang."
If you understand that phrase, i-email mo'ko. ;-)

- Raw text -


  webmaster     delorie software   privacy  
  Copyright © 2019   by DJ Delorie     Updated Jul 2019