Date: Sun, 6 Jun 1999 10:50:05 +0300 (IDT) From: Eli Zaretskii X-Sender: eliz AT is To: Robert Hoehne , Andris Pavenis cc: djgpp-workers AT delorie DOT com Subject: Debugging and FP math Message-ID: MIME-Version: 1.0 Content-Type: TEXT/PLAIN; charset=US-ASCII Reply-To: djgpp-workers AT delorie DOT com I guess nobody tried to run a program that uses FP math under a debugger that was linked with the v2.03 version of libdbg.a. If you do, you'll find all FP operations totally messed up. The problem is that hook_dpmi() calls load_npx() when the debuggee is run the first time, but the npx variable is all-zeroes at this stage, since that is how edi_init() initializes it. This puts the FPU into a weird state (like 24-bit operation mode), and it stays that way, since this zeroed npx state is saved after the child stops and is restored before it is resumed. I attach below patches that fix this, at least temporarily, by initializing the npx variable with the debugger's FPU state inside edi_init(). (For the reasons for ``temporarily'' see the last item below.) While working on this problem, I bumped into several related issues: - go32-nat.c, in its pre-v2.03 code, uses save_npx and load_npx the other way around: it saves and restores the *debugger's* FPU state across the debuggee's invocations. This causes commands like "info float" in GDB to display the debugger's FPU state. I think this is wrong, so I changed that (the diffs are sent in a separate message, since they don't belong to the DJGPP source tree). - For some strange reason, dbgcom.c used memcpy to assign int values to an array of ints, which also forced funny pointer arithmetics. I changed that to use normal array assignments, se below. - Here's a part of the source of save_npx: asm ("inb $0xa0, %%al testb $0x20, %%al jz 1f xorb %%al, %%al outb %%al, $0xf0 movb $0x20, %%al outb %%al, $0xa0 outb %%al, $0x20 1: fnsave %0 fwait" : "=m" (npx) : /* No input */ : "%eax"); If I understand correctly, the part before label 1 wants to see whether IRQ13 line is activated in the secondary interrupt controller, and if so, clear the FPU BUSY line and EOI both of the interrupt controllers. However, my references indicate that before reading from port 0Ah, we need to issue a command that specifies which one of the registers of the interrupt controller (Interrupt Request register or Interrupt-in-Service register) we want to read. I guess we want the latter, because EOI clears it. So I added the two instructions to send the 0Bh command to the slave controller (this change is also part of the diffs below). - Shouldn't we also save and restore the FPU state of the debugger? Suppose the debugged program changes the FPU mode of operation by setting its control word--we don't want the debugger to be affected by that, right? So we would need to have two NPX variables: one each for the debugger and the debuggee, and switch between them before running the debuggee and after it stops at a breakpoint. Comments? *** src/debug/common/dbgcom.c~8 Mon May 24 22:12:08 1999 --- src/debug/common/dbgcom.c Sat Jun 5 10:41:38 1999 *************** unsigned short dos_descriptors[DOS_DESCR *** 53,59 **** ss can be different from ds in dbgsig !! */ static int excep_stack[1000]; static int errcode,cs,eflags,eip,ss,esp,ret_cs,ret_eip; ! static void *cur_pos; static int child_exception_level; ExternalDebuggerInfo edi; --- 53,59 ---- ss can be different from ds in dbgsig !! */ static int excep_stack[1000]; static int errcode,cs,eflags,eip,ss,esp,ret_cs,ret_eip; ! static int *cur_pos; static int child_exception_level; ExternalDebuggerInfo edi; *************** void save_npx (void) *** 97,103 **** int i; if ((__dpmi_get_coprocessor_status() & FPU_PRESENT) == 0) return; ! asm ("inb $0xa0, %%al testb $0x20, %%al jz 1f xorb %%al, %%al --- 97,105 ---- int i; if ((__dpmi_get_coprocessor_status() & FPU_PRESENT) == 0) return; ! asm ("movb $0x0b, %%al ! outb %%al, $0xa0 ! inb $0xa0, %%al testb $0x20, %%al jz 1f xorb %%al, %%al *************** static void call_app_exception(int signu *** 942,956 **** eflags = load_state->__eflags; /* reset the debug trace bit */ /* we don't want to step inside the exception_table code */ ! load_state->__eflags &= 0xfffffeff; errcode = load_state->__sigmask; load_state->__eip=app_handler[signum].offset32; load_state->__cs=app_handler[signum].selector; /* use our own exception stack */ child_exception_level++; memset(&excep_stack,0xAB,sizeof(excep_stack)); ! cur_pos = &excep_stack[1000-40]; ! cur_pos -= 8*4; load_state->__ss = my_ds; load_state->__esp= (int) cur_pos; /* where to return */ --- 944,957 ---- eflags = load_state->__eflags; /* reset the debug trace bit */ /* we don't want to step inside the exception_table code */ ! load_state->__eflags &= 0xfffffeffU; errcode = load_state->__sigmask; load_state->__eip=app_handler[signum].offset32; load_state->__cs=app_handler[signum].selector; /* use our own exception stack */ child_exception_level++; memset(&excep_stack,0xAB,sizeof(excep_stack)); ! cur_pos = &excep_stack[1000-40] - 8; load_state->__ss = my_ds; load_state->__esp= (int) cur_pos; /* where to return */ *************** static void call_app_exception(int signu *** 959,980 **** ret_eip = (int) &dbgcom_exception_return_complete; else ret_eip = (int) &dbgcom_exception_return; ! memcpy(cur_pos,&ret_eip,4); ! cur_pos+=4; ! memcpy(cur_pos,&ret_cs,4); ! cur_pos+=4; ! memcpy(cur_pos,&errcode,4); ! cur_pos+=4; ! memcpy(cur_pos,&eip,4); ! cur_pos+=4; ! memcpy(cur_pos,&cs,4); ! cur_pos+=4; ! memcpy(cur_pos,&eflags,4); ! cur_pos+=4; ! memcpy(cur_pos,&esp,4); ! cur_pos+=4; ! memcpy(cur_pos,&ss,4); ! cur_pos+=4; longjmp(load_state, load_state->__eax); } --- 960,974 ---- ret_eip = (int) &dbgcom_exception_return_complete; else ret_eip = (int) &dbgcom_exception_return; ! cur_pos[0] = ret_eip; ! cur_pos[1] = ret_cs; ! cur_pos[2] = errcode; ! cur_pos[3] = eip; ! cur_pos[4] = cs; ! cur_pos[5] = eflags; ! cur_pos[6] = esp; ! cur_pos[7] = ss; ! cur_pos += 8; longjmp(load_state, load_state->__eax); } *************** void edi_init(jmp_buf start_state) *** 1251,1256 **** --- 1245,1251 ---- app_cs = a_tss.tss_cs; edi.app_base = 0; memset(&npx,0,sizeof(npx)); + save_npx(); /* FIXME!! */ /* Save all the changed signal handlers */ oldTRAP = signal(SIGTRAP, dbgsig); oldSEGV = signal(SIGSEGV, dbgsig);