patch for printing 64-bit values in i386 registers; STABS format

Colin Smith
2003-04-24T22:31:54Z

* Description of bug:

On i386 with STABS debug format, if the debug info for a function
indicates that a long long variable is held in a register, for example
%ebx, GDB will assume that the other register follows the first one
in gdb register number order. But GDB register numbering and GCC
register numbering do not correspond.

The patch fixes this by offering a new macro that will compute the
"next" register given the first one in the event a value is spread
across multiple registers. We implement this macro in the i386
case.

* Changelog Entry

2003-04-24 Colin Smith <colins@google.com>

* findvar.c (value_from_register): use new NEXT_LOCAL_REGNUM to
find follow-on registers for values larger than one register, in
the STABS case. Implemented in tm-i386.h and i386-tdep.c.

* Example code that provokes the bug:

#include <iostream>
//using namespace std;

typedef long long uint64;

inline uint64 g(uint64 v, uint64 w) {
cout << v << ' ' << w << endl;
++w;
return v-w;
}

int k = 3;

main() {
uint64 v = 0x1111111122222222;

if (v > 0) {
uint64 u = 0xaaaaaaaabbbbbbbb;

for (int i = 0; i < k; ++i)
v = g(u+v,v);
}
}

... compile with gcc (2.95.x), -O0, -g; set breakpoint on 'g', run,
'print w', note that value is incorrect; do 'i addr w', see that
it's supposed to be in %ebx, do 'i regi' and see that GDB thinks
that the value is in %esp:%ebx, when in fact it's in %esi:%ebx.

* Text of Patch.

diff -cpr gdb-orig/gdb/config/i386/tm-i386.h gdb/gdb/config/i386/tm-i386.h
*** gdb-orig/gdb/config/i386/tm-i386.h Thu Apr 24 15:27:35 2003
--- gdb/gdb/config/i386/tm-i386.h Thu Apr 24 13:06:52 2003
***************
*** 22,27 ****
--- 22,36 ----
#ifndef TM_I386_H
#define TM_I386_H 1

+ /* This macro maps between GCC and GDB's register ordering. This is
+ used when fetching quantities larger than one register out of the
+ register file: GDB's register ordering is not indicative of the
+ order in which these registers would have been allocated by the
+ compiler. */
+
+ #define NEXT_LOCAL_REGNUM(regnum) \
+ i386_next_local_regnum (regnum)
+
#define GDB_MULTI_ARCH GDB_MULTI_ARCH_PARTIAL

/* FIXME: kettenis/2000-06-12: These do not belong here. */
diff -cpr gdb-orig/gdb/findvar.c gdb/gdb/findvar.c
*** gdb-orig/gdb/findvar.c Thu Apr 24 15:27:32 2003
--- gdb/gdb/findvar.c Thu Apr 24 12:29:06 2003
***************
*** 46,51 ****
--- 46,62 ----
you lose
#endif

+ /* GCC's register ordering (the ordering it will follow when
+ allocating 64-bit quantities among multiple registers) is not
+ necessarily the same as GDB's register numbering. In the event
+ that these orderings don't agree, a config file can specify
+ an alternate implementation of this macro. (This case occurs
+ on gcc2 x i386, for example.) */
+
+ #ifndef NEXT_LOCAL_REGNUM
+ #define NEXT_LOCAL_REGNUM(regnum) ((regnum)+1)
+ #endif
+
LONGEST
extract_signed_integer (const void *addr, int len)
{
*************** value_from_register (struct type *type,
*** 735,744 ****
}
else
#endif /* GDB_TARGET_IS_H8500 */
for (local_regnum = regnum;
value_bytes_copied < len;
(value_bytes_copied += REGISTER_RAW_SIZE (local_regnum),
! ++local_regnum))
{
get_saved_register (value_bytes + value_bytes_copied,
&optim,
--- 746,756 ----
}
else
#endif /* GDB_TARGET_IS_H8500 */
+
for (local_regnum = regnum;
value_bytes_copied < len;
(value_bytes_copied += REGISTER_RAW_SIZE (local_regnum),
! local_regnum = NEXT_LOCAL_REGNUM (local_regnum)))
{
get_saved_register (value_bytes + value_bytes_copied,
&optim,
diff -cpr gdb-orig/gdb/i386-tdep.c gdb/gdb/i386-tdep.c
*** gdb-orig/gdb/i386-tdep.c Thu Apr 24 15:27:33 2003
--- gdb/gdb/i386-tdep.c Thu Apr 24 12:59:01 2003
*************** static char *i386_register_names[] =
*** 58,63 ****
--- 58,86 ----
"mxcsr"
};

+ int
+ i386_next_local_regnum (int reg)
+ {
+ /* Interpretation of the following table: because
+ next_reg_map[regnum(ebx)] == regnum(esi), esi
+ follows ebx in multi-register data allocation
+ according to GCC. No register follows esp, since
+ esp is dedicated to another important function ;-) */
+
+ /* eax ecx edx ebx esp ebp esi edi */
+ static int next_reg_map [] = { 2, 3, 1, 6, -1, 4, 7, 5 };
+ int n;
+
+ if (reg >= 0 && reg < 8)
+ if ((n = next_reg_map[reg]) >= 0)
+ return n;
+
+ /* Give up: we'll have to try gdb's standard algorithm. We've
+ done no harm, though. */
+
+ return reg+1;
+ }
+
/* MMX registers. */

static char *i386_mmx_names[] =