Debugging a frameless function

Eli Zaretskii
2009-04-29T18:29:07Z

I'm trying to debug a problem in the DJGPP build of a recent GDB
snapshot, whereby stepping into a function compiled with
"-fomit-frame-pointer" throws an error:

Cannot access memory at address 0x12

I reproduce below the test program and the transcript of a GDB session
where I try to debug this. Any hints or ideas are welcome. TIA

From what I see, a call to read_reg in dwarf2_frame_cache, that tries
to read the value of EBP, ends up calling
sentinel_frame_prev_register, and from there eventually
target_fetch_registers is called. But since we are in a frameless
function, EBP holds a variable, not the frame pointer, so the value
fetched by target_fetch_registers is 2, which later causes
dwarf2_frame_this_id to call frame_id_build with the first argument
whose value is 0x16, so the address of the stack in that frame is
bogus. And that is what causes the above error message.

When I run the same program on GNU/Linux, the problem does not
happen. But the GNU/Linux box to which I have access is a 64-bit
machine, so the architecture-dependent bits there are different, and I
cannot easily compare what the code does on these two systems.

My question is, how should GDB overcome the difficulty of a frameless
function, when it tries to unwind a frame where EBP is used for
holding a variable? Can someone please spot any missing pieces in the
session below, and give me hints on where to look for the source of
the trouble?

I see other failures in the DJGPP port that somehow seem related to
dwarf2-frame.c functions, so I believe solving this problem will let
me solve others as well. The problem doesn't happen in the same
program compiled with -gstabs+ rather than using DWARF2 debug info.

Here's the program I used. Compile it with -g -O -fomit-frame-pointer
to get the argument `n' of the function `foo' stored in EBP.

---------------------------------
#include <stdio.h>
#include <stdlib.h>

int
foo (register int n)
{
register int i;
register int a = n * 1;
register int b = n * 2;
register int c = n * 3;
register int d = n * 4;
register int e = n * 5;

for (i = 0; i < n; i++)
{
if (i & 1) a = b + c; else a = c + e;
if (i & 2) b = c + d; else b = d + a;
if (i & 4) c = d + e; else c = e + b;
if (i & 8) d = e + a; else d = a + c;
if (i & 16) e = a + b; else e = b + d;
}

return a + b + c + d + e;
}

int
main (int argc, char **argv)
{
if (argc == 2)
printf ("%d\n", foo (atoi (argv[1])));
}

---------------------------

And here is the debugging session mentioned above. I use GDB 6.1 to
debug the April 10th snapshot:

D:\usr\djgpp\gnu\gdb-68.410\gdb>d:\usr\djgpp\bin\gdb ./gdb.exe
GNU gdb 6.1
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "--host=i386-pc-msdosdjgpp --target=djgpp"...
Setting up the environment for debugging gdb.
During symbol reading, debug info gives macro definition outside of any file: __STDC_HOSTED__ 1.
During symbol reading, no terminating 0-type entry for macros in `.debug_macinfo' section.
Breakpoint 1 at 0x660f: file utils.c, line 993.
Breakpoint 2 at 0xaae1: file .././gdb/cli/cli-cmds.c, line 199.
(top-gdb) break dwarf2_frame_cache
Breakpoint 3 at 0x11f9f2: file dwarf2-frame.c, line 874.
(top-gdb) r --nx ./frame_d.exe
Starting program: d:/usr/djgpp/gnu/gdb-68.410/gdb/./gdb.exe --nx ./frame_d.exe
GNU gdb (GDB) 6.8.50.20090410
Copyright (C) 2009 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "--host=i586-pc-msdosdjgpp --target=djgpp".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
(gdb) break foo
Breakpoint 1 at 0x1680: file frameless.c, line 6.
(gdb) r 2
Starting program: d:/usr/djgpp/gnu/gdb-68.410/gdb/./frame_d.exe 2

Breakpoint 3, dwarf2_frame_cache (this_frame=0x418dec, this_cache=0x418df0)
at dwarf2-frame.c:874
874 struct gdbarch *gdbarch = get_frame_arch (this_frame);
(top-gdb) c
Continuing.
Breakpoint 1, foo (n=588648) at frameless.c:6
6 {
(gdb) n

Breakpoint 3, dwarf2_frame_cache (this_frame=0x418dec, this_cache=0x418df0)
at dwarf2-frame.c:874
874 struct gdbarch *gdbarch = get_frame_arch (this_frame);
(top-gdb) n
875 const int num_regs = gdbarch_num_regs (gdbarch)
(top-gdb) until 932
932 execute_cfa_program (fde, fde->instructions, fde->end, this_frame,
fs);
(top-gdb) n
935 switch (fs->cfa_how)
(top-gdb)
938 cache->cfa = read_reg (this_frame, fs->cfa_reg);
(top-gdb) s
read_reg (baton=0x418dec, reg=5) at dwarf2-frame.c:277
277 struct gdbarch *gdbarch = get_frame_arch (this_frame);
(top-gdb) n
281 regnum = gdbarch_dwarf2_reg_to_regnum (gdbarch, reg);
(top-gdb) n
283 buf = alloca (register_size (gdbarch, regnum));
(top-gdb) p regnum
$10 = 5
(top-gdb) n
284 get_frame_register (this_frame, regnum, buf);
(top-gdb) s
get_frame_register (frame=0x20, regnum=5, buf=0x3e84d0 "\005") at frame.c:653
653 frame_unwind_register (frame->next, regnum, buf);
(top-gdb) s
frame_unwind_register (frame=0x418da0, regnum=5, buf=0x3e84d0 "\005")
at frame.c:645
645 frame_register_unwind (frame, regnum, &optimized, &lval, &addr,
(top-gdb) s
frame_register_unwind (frame=0x418da0, regnum=5, optimizedp=0x3e8488,
lvalp=0x3e848c, addrp=0x3e8490, realnump=0x3e8494, bufferp=0x3e84d0 "\005")
at frame.c:593
593 gdb_assert (optimizedp != NULL);
(top-gdb) n
594 gdb_assert (lvalp != NULL);
(top-gdb)
595 gdb_assert (addrp != NULL);
(top-gdb)
596 gdb_assert (realnump != NULL);
(top-gdb)
599 value = frame_unwind_register_value (frame, regnum);
(top-gdb) s
frame_unwind_register_value (frame=0x418da0, regnum=5) at frame.c:661
661 gdb_assert (frame != NULL);
(top-gdb) n
663 if (frame_debug)
(top-gdb)
673 if (frame->unwind == NULL)
(top-gdb)
677 value = frame->unwind->prev_register (frame, &frame->prologue_cache
, regnum);
(top-gdb) s
sentinel_frame_prev_register (this_frame=0x418da0,
this_prologue_cache=0xbc9a4, regnum=5) at sentinel-frame.c:50
50 struct gdbarch *gdbarch = get_frame_arch (this_frame);
(top-gdb) n
51 struct frame_unwind_cache *cache = *this_prologue_cache;
(top-gdb)
55 value = allocate_value (register_type (gdbarch, regnum));
(top-gdb)
56 VALUE_LVAL (value) = lval_register;
(top-gdb)
57 VALUE_REGNUM (value) = regnum;
(top-gdb)
58 VALUE_FRAME_ID (value) = get_frame_id (this_frame);
(top-gdb)
63 regcache_cooked_read (cache->regcache, regnum, value_contents_raw (v
alue));
(top-gdb) s
value_contents_raw (value=0x439ed0) at value.c:399
399 allocate_value_contents (value);
(top-gdb) n
400 return value->contents + value->embedded_offset;
(top-gdb) p value
$12 = (struct value *) 0x439ed0
(top-gdb) p *value
$13 = {lval = lval_register, modifiable = 1, location = {address = 0,
internalvar = 0x0, computed = {funcs = 0x0, closure = 0x0}}, offset = 0,
bitsize = 0, bitpos = 0, frame_id = {stack_addr = 0, code_addr = 0,
special_addr = 0, stack_addr_p = 0, code_addr_p = 0, special_addr_p = 0},
type = 0x44ee48, enclosing_type = 0x44ee48, embedded_offset = 0,
pointed_to_offset = 0, next = 0x0, regnum = 5, lazy = 0 '\0',
optimized_out = 0 '\0', initialized = 1, contents = 0x439f28 ""}
(top-gdb) n
401 }
(top-gdb) s
regcache_cooked_read (regcache=0x44f4d8, regnum=5, buf=0x439f28 "")
at regcache.c:581
581 gdb_assert (regnum >= 0);
(top-gdb) n
582 gdb_assert (regnum < regcache->descr->nr_cooked_registers);
(top-gdb) p regcache->descr
$14 = (struct regcache_descr *) 0x435dcc
(top-gdb) p *regcache->descr
$15 = {gdbarch = 0x435b00, nr_raw_registers = 32, sizeof_raw_registers = 240,
sizeof_raw_register_valid_p = 40, nr_cooked_registers = 40,
sizeof_cooked_registers = 240, sizeof_cooked_register_valid_p = 40,
register_offset = 0x435fa8, sizeof_register = 0x435f08,
register_type = 0x435df4}
(top-gdb) n
583 if (regnum < regcache->descr->nr_raw_registers)
(top-gdb)
584 regcache_raw_read (regcache, regnum, buf);
(top-gdb) s
regcache_raw_read (regcache=0x44f4d8, regnum=5, buf=0x439f28 "")
at regcache.c:500
500 gdb_assert (regcache != NULL && buf != NULL);
(top-gdb) n
501 gdb_assert (regnum >= 0 && regnum < regcache->descr->nr_raw_registe
rs);
(top-gdb)
506 if (!regcache->readonly_p)
(top-gdb)
508 if (!regcache_valid_p (regcache, regnum))
(top-gdb)
510 struct cleanup *old_chain = save_inferior_ptid ();
(top-gdb)
511 inferior_ptid = regcache->ptid;
(top-gdb)
512 target_fetch_registers (regcache, regnum);
(top-gdb) p regnum
$17 = 5
(top-gdb) s
target_fetch_registers (regcache=0x44f4d8, regno=5) at target.c:2744
2744 for (t = current_target.beneath; t != NULL; t = t->beneath)
(top-gdb) s
2746 if (t->to_fetch_registers != NULL)
(top-gdb)
2748 t->to_fetch_registers (t, regcache, regno);
(top-gdb)
go32_fetch_registers (ops=0x347828, regcache=0x44f4d8, regno=5)
at go32-nat.c:490
490 if (regno >= 0)
(top-gdb) n
491 fetch_register (regcache, regno);
(top-gdb)
500 }
(top-gdb)
target_fetch_registers (regcache=0x44f4d8, regno=5) at target.c:2749
2749 if (targetdebug)
(top-gdb)
2754 }
(top-gdb)
regcache_raw_read (regcache=0x44f4d8, regnum=5, buf=0x439f28 "")
at regcache.c:513
513 do_cleanups (old_chain);
(top-gdb)
526 memcpy (buf, register_buffer (regcache, regnum),
(top-gdb)
528 }
(top-gdb)
regcache_cooked_read (regcache=0x5, regnum=5, buf=0x439f28 "\002")
at regcache.c:594
594 }
(top-gdb)
sentinel_frame_prev_register (this_frame=0x418da0, this_prologue_cache=0x5,
regnum=5) at sentinel-frame.c:66
66 }
(top-gdb)
frame_unwind_register_value (frame=0x418da0, regnum=5) at frame.c:679
679 if (frame_debug)
(top-gdb)
714 }
(top-gdb)
frame_register_unwind (frame=0x418da0, regnum=5, optimizedp=0x3e8488,
lvalp=0x3e848c, addrp=0x3e8490, realnump=0x3e8494, bufferp=0x3e84d0 "\005")
at frame.c:601
601 gdb_assert (value != NULL);
(top-gdb)
603 *optimizedp = value_optimized_out (value);
(top-gdb)
604 *lvalp = VALUE_LVAL (value);
(top-gdb)
605 *addrp = VALUE_ADDRESS (value);
(top-gdb)
606 *realnump = VALUE_REGNUM (value);
(top-gdb)
608 if (bufferp)
(top-gdb)
609 memcpy (bufferp, value_contents_all (value),
(top-gdb)
614 release_value (value);
(top-gdb)
615 value_free (value);
(top-gdb)
616 }
(top-gdb)
frame_unwind_register (frame=0x418da0, regnum=5, buf=0x3e84d0 "\002")
at frame.c:647
647 }
(top-gdb)
get_frame_register (frame=0x0, regnum=5, buf=0x3e84d0 "\002") at frame.c:654
654 }
(top-gdb)
read_reg (baton=0x418dec, reg=5) at dwarf2-frame.c:291
291 return unpack_long (register_type (gdbarch, regnum), buf);
(top-gdb)
292 }
(top-gdb)
dwarf2_frame_cache (this_frame=0x418dec, this_cache=0x418df0)
at dwarf2-frame.c:939
939 if (fs->armcc_cfa_offsets_reversed)
(top-gdb) l
934 /* Calculate the CFA. */
935 switch (fs->cfa_how)
936 {
937 case CFA_REG_OFFSET:
938 cache->cfa = read_reg (this_frame, fs->cfa_reg);
939 if (fs->armcc_cfa_offsets_reversed)
940 cache->cfa -= fs->cfa_offset;
941 else
942 cache->cfa += fs->cfa_offset;
943 break;
(top-gdb) p cache->cfa
$18 = 2
(top-gdb) c
Continuing.

Breakpoint 3, dwarf2_frame_cache (this_frame=0x418dec, this_cache=0x418df0)
at dwarf2-frame.c:874
874 struct gdbarch *gdbarch = get_frame_arch (this_frame);
(top-gdb) c
Continuing.
Cannot access memory at address 0x12