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" Subject: Core dumping for DJGPP programs Cc: sandmann AT clio DOT rice DOT edu Mime-Version: 1.0 Content-Type: text/plain; charset="us-ascii" 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. ;-)