Re: [RFC] Gdb line table implementation tweak

Jim Blandy
2002-02-21T20:50:27Z

Holey moley! This patch is approved, sir.

Fred Fish <fnf@public.ninemoons.com> writes:

> The implementation of gdb's line number table can cause gdb to give
> misleading results when some parts of a program are compiled with
> debugging enabled and other parts are not. Here is an example:
>
> $ cat Makefile
> CXX = g++
>
> all: p p.stabs p1.o.stabs
>
> p: p1.o p2.o
> $(CXX) -o p p1.o p2.o
>
> p1.o: p1.s
> $(CXX) -c p1.s
>
> p2.o: p2.s
> $(CXX) -c p2.s
>
> p1.s: p1.cpp
> $(CXX) -g -S p1.cpp
>
> p2.s: p2.cpp
> $(CXX) -S p2.cpp
>
> p.stabs: p
> objdump --stabs p >p.stabs
>
> p1.o.stabs: p1.o
> objdump --stabs p1.o >p1.o.stabs
>
> clean:
> rm -f p1.o p2.o p1.s p2.s p Makefile~ *.syms *.stabs
>
>
> $ cat p1.cpp
> #include <stdio.h>
>
> class MainClass
> {
> public:
> MainClass() {};
> ~MainClass() {};
> virtual void main();
> };
>
> void MainClass::main()
> {
> }
>
> int main(int argc, char** argv)
> {
> extern void subr (int);
> subr (5);
> }
>
> $ cat p2.cpp
> #include <stdio.h>
>
> void subr (int x)
> {
> printf ("x = %d\n", x);
> }
> $
>
> If we run make to build executable 'p', where p1.o is compiled with
> debugging enabled and p2.o is compiled without debugging, gdb gets
> confused about what file subr() is in. It thinks it is in p1.cpp,
> when it is really in p2.cpp:
>
> $make
> g++ -g -S p1.cpp
> g++ -c p1.s
> g++ -S p2.cpp
> g++ -c p2.s
> g++ -o p p1.o p2.o
> objdump --stabs p >p.stabs
> objdump --stabs p1.o >p1.o.stabs
> $ /usr/sourceware/bin/gdb p
> GNU gdb 2002-02-12-cvs
> Copyright 2002 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 "i686-pc-linux-gnu"...
> (gdb) br *0x8048634
> Breakpoint 1 at 0x8048634: file p1.cpp, line 19.
> (gdb) x/i 0x8048634
> 0x8048634 <subr__Fi>: push %ebp
> (gdb) run
> Starting program: /cygnus/cases/106539/example5-linux/p
>
> Breakpoint 1, 0x08048634 in subr () at p1.cpp:19
> 19 }
> (gdb) bt
> #0 0x08048634 in subr () at p1.cpp:19
> #1 0x400b2306 in __libc_start_main (main=0x8048618 , argc=1, ubp_av=0xbfffeef4, init=0x8048474 <_init>, fini=0x80486f8 <_fini>, rtld_fini=0x4000d2dc <_dl_fini>,
> stack_end=0xbfffeeec) at ../sysdeps/generic/libc-start.c:129
> (gdb) quit
> The program is running. Exit anyway? (y or n) y
> $
>
> Note in the above, setting the breakpoint at the first instruction of
> subr() appears to put it at line 19 in p1.cpp. When the program is
> run and gdb stops at subr(), which is actually in p2.cpp, it prints it
> wrong again. And in the backtrace, gdb gets the right function, but
> the wrong file and line number.
>
> One way to fix this is to slightly change the line table such that it
> allows gdb to know what ranges of PC's represent ranges for which line
> number info is valid and which don't. An easy way to do that is to
> use an entry with a line number of zero to mark ranges that have no
> valid line number info.
>
> For example, the line table for p1.cpp is:
>
> Line table:
>
> line 12 at 0x8048610
> line 13 at 0x8048616
> line 16 at 0x8048618
> line 18 at 0x804861e
> line 19 at 0x804862b
> line 9 at 0x8048690
> line 19 at 0x8048696
> line 6 at 0x80486bc
> line 7 at 0x80486cc
>
> After applying the attached patch, gdb's internal representation of
> the line table for p1.cpp changes to the following:
>
> Line table:
>
> line 12 at 0x8048610
> line 13 at 0x8048616
> line 0 at 0x8048618
> line 16 at 0x8048618
> line 18 at 0x804861e
> line 19 at 0x804862b
> line 0 at 0x8048632
> line 9 at 0x8048690
> line 19 at 0x8048696
> line 0 at 0x80486bb
> line 6 at 0x80486bc
> line 0 at 0x80486ca
> line 7 at 0x80486cc
> line 0 at 0x80486f5
>
> And rerunning gdb on the test case produces:
>
> $ /tmp/gdb p
> GNU gdb 2002-02-12-cvs
> Copyright 2002 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 "i686-pc-linux-gnu"...
> (gdb) br *0x8048634
> Breakpoint 1 at 0x8048634
> (gdb) x/i 0x8048634
> 0x8048634 <subr__Fi>: push %ebp
> (gdb) run
> Starting program: /cygnus/cases/106539/example5-linux/p
>
> Breakpoint 1, 0x08048634 in subr ()
> (gdb) bt
> #0 0x08048634 in subr ()
> #1 0x400b2306 in __libc_start_main (main=0x8048618 , argc=1, ubp_av=0xbfffe674, init=0x8048474 <_init>, fini=0x80486f8 <_fini>, rtld_fini=0x4000d2dc <_dl_fini>,
> stack_end=0xbfffe66c) at ../sysdeps/generic/libc-start.c:129
> (gdb) quit
> The program is running. Exit anyway? (y or n) y
> $
>
> It might be a little easier to understand how the line table describes
> the entire range of PC's from it's lowest to highest if we match up
> the new line table entries with a disassembly produced by gdb. Note
> in the following that now gdb knows which parts actually come from
> p1.cpp (a range starting with a nonzero line number) and which parts
> have no valid line info (a range starting with a zero line number):
>
>
> line 12 at 0x8048610 0x8048610 <main__9MainClass>: push %ebp
> 0x8048611 <main__9MainClass+1>: mov %esp,%ebp
> 0x8048613 <main__9MainClass+3>: mov 0x8(%ebp),%eax
> line 13 at 0x8048616 0x8048616 <main__9MainClass+6>: pop %ebp
> 0x8048617 <main__9MainClass+7>: ret
> line 0 at 0x8048618
> line 16 at 0x8048618 0x8048618 : push %ebp
> 0x8048619 <main+1>: mov %esp,%ebp
> 0x804861b <main+3>: sub $0x8,%esp
>
> line 18 at 0x804861e 0x804861e <main+6>: sub $0xc,%esp
> 0x8048621 <main+9>: push $0x5
> 0x8048623 <main+11>: call 0x8048634 <subr__Fi>
> 0x8048628 <main+16>: add $0x10,%esp
>
> line 19 at 0x804862b 0x804862b <main+19>: mov $0x0,%eax
> 0x8048630 <main+24>: leave
> 0x8048631 <main+25>: ret
>
> line 0 at 0x8048632 0x8048632 <main+26>: mov %esi,%esi
> 0x8048634 <subr__Fi>: push %ebp
> 0x8048635 <subr__Fi+1>: mov %esp,%ebp
> 0x8048637 <subr__Fi+3>: sub $0x8,%esp
> 0x804863a <subr__Fi+6>: sub $0x8,%esp
> 0x804863d <subr__Fi+9>: pushl 0x8(%ebp)
> 0x8048640 <subr__Fi+12>: push $0x804872b
> 0x8048645 <subr__Fi+17>: call 0x80484cc <printf>
> 0x804864a <subr__Fi+22>: add $0x10,%esp
> 0x804864d <subr__Fi+25>: leave
> 0x804864e <subr__Fi+26>: ret
> 0x804864f <subr__Fi+27>: nop
> 0x8048650 <__do_global_ctors_aux>: push %ebp
> 0x8048651 <__do_global_ctors_aux+1>: mov %esp,%ebp
> 0x8048653 <__do_global_ctors_aux+3>: push %ebx
> 0x8048654 <__do_global_ctors_aux+4>: sub $0x4,%esp
> 0x8048657 <__do_global_ctors_aux+7>: mov 0x804978c,%eax
> 0x804865c <__do_global_ctors_aux+12>: mov $0x804978c,%ebx
> 0x8048661 <__do_global_ctors_aux+17>: cmp $0xffffffff,%eax
> 0x8048664 <__do_global_ctors_aux+20>: je 0x804867c <__do_global_ctors_aux+44>
> 0x8048666 <__do_global_ctors_aux+22>: lea 0x0(%esi),%esi
> 0x8048669 <__do_global_ctors_aux+25>: lea 0x0(%edi,1),%edi
> 0x8048670 <__do_global_ctors_aux+32>: sub $0x4,%ebx
> 0x8048673 <__do_global_ctors_aux+35>: call *%eax
> 0x8048675 <__do_global_ctors_aux+37>: mov (%ebx),%eax
> 0x8048677 <__do_global_ctors_aux+39>: cmp $0xffffffff,%eax
> 0x804867a <__do_global_ctors_aux+42>: jne 0x8048670 <__do_global_ctors_aux+32>
> 0x804867c <__do_global_ctors_aux+44>: pop %eax
> 0x804867d <__do_global_ctors_aux+45>: pop %ebx
> 0x804867e <__do_global_ctors_aux+46>: pop %ebp
> 0x804867f <__do_global_ctors_aux+47>: ret
> 0x8048680 <init_dummy>: push %ebp
> 0x8048681 <init_dummy+1>: mov %esp,%ebp
> 0x8048683 <init_dummy+3>: sub $0x8,%esp
> 0x8048686 <init_dummy+6>: mov %ebp,%esp
> 0x8048688 <init_dummy+8>: pop %ebp
> 0x8048689 <init_dummy+9>: ret
> 0x804868a <init_dummy+10>: lea 0x0(%esi),%esi
>
> line 9 at 0x8048690 0x8048690 <__tf9MainClass>: push %ebp
> 0x8048691 <__tf9MainClass+1>: mov %esp,%ebp
> 0x8048693 <__tf9MainClass+3>: sub $0x8,%esp
>
> line 19 at 0x8048696 0x8048696 <__tf9MainClass+6>: cmpl $0x0,0x80498b8
> 0x804869d <__tf9MainClass+13>: jne 0x80486b4 <__tf9MainClass+36>
> 0x804869f <__tf9MainClass+15>: sub $0x8,%esp
> 0x80486a2 <__tf9MainClass+18>: push $0x8048720
> 0x80486a7 <__tf9MainClass+23>: push $0x80498b8
> 0x80486ac <__tf9MainClass+28>: call 0x804849c <__rtti_user>
> 0x80486b1 <__tf9MainClass+33>: add $0x10,%esp
> 0x80486b4 <__tf9MainClass+36>: mov $0x80498b8,%eax
> 0x80486b9 <__tf9MainClass+41>: leave
> 0x80486ba <__tf9MainClass+42>: ret
>
> line 0 at 0x80486bb 0x80486bb <__tf9MainClass+43>: nop
>
> line 6 at 0x80486bc 0x80486bc <__9MainClass>: push %ebp
> 0x80486bd <__9MainClass+1>: mov %esp,%ebp
> 0x80486bf <__9MainClass+3>: mov 0x8(%ebp),%eax
> 0x80486c2 <__9MainClass+6>: movl $0x8049748,(%eax)
> 0x80486c8 <__9MainClass+12>: pop %ebp
> 0x80486c9 <__9MainClass+13>: ret
>
> line 0 at 0x80486ca 0x80486ca <__9MainClass+14>: mov %esi,%esi
>
> line 7 at 0x80486cc 0x80486cc <_._9MainClass>: push %ebp
> 0x80486cd <_._9MainClass+1>: mov %esp,%ebp
> 0x80486cf <_._9MainClass+3>: sub $0x8,%esp
> 0x80486d2 <_._9MainClass+6>: mov 0xc(%ebp),%eax
> 0x80486d5 <_._9MainClass+9>: mov 0x8(%ebp),%edx
> 0x80486d8 <_._9MainClass+12>: movl $0x8049748,(%edx)
> 0x80486de <_._9MainClass+18>: and $0x1,%eax
> 0x80486e1 <_._9MainClass+21>: test %al,%al
> 0x80486e3 <_._9MainClass+23>: je 0x80486f3 <_._9MainClass+39>
> 0x80486e5 <_._9MainClass+25>: sub $0xc,%esp
> 0x80486e8 <_._9MainClass+28>: pushl 0x8(%ebp)
> 0x80486eb <_._9MainClass+31>: call 0x80484ec <__builtin_delete>
> 0x80486f0 <_._9MainClass+36>: add $0x10,%esp
> 0x80486f3 <_._9MainClass+39>: leave
> 0x80486f4 <_._9MainClass+40>: ret
>
> line 0 at 0x80486f5 0x80486f5 <_._9MainClass+41>: lea 0x0(%esi),%esi
>
> The patch to make this work for stabs is very simple and supplied
> below for discussion purposes. I presume it would be fairly easy to
> also change the other debug info readers to do the same thing.
> Obviously someday we would like to have gdb take full advantage of
> more expressive formats like DWARF, but for now this patch seems to
> have substantial advantages.
>
> I did run before and after testing with the gdb testsuite and it
> didn't show any regressions, or improvements for that matter, but that
> is to be expected since we don't actually test for functionality with
> mixed levels of debugging information.
>
> -Fred
>
> Index: ChangeLog
> ===================================================================
> RCS file: /cvs/src/src/gdb/ChangeLog,v
> retrieving revision 1.2184
> diff -c -p -r1.2184 ChangeLog
> *** ChangeLog 2002/02/12 00:59:27 1.2184
> --- ChangeLog 2002/02/13 00:22:47
> ***************
> *** 1,3 ****
> --- 1,14 ----
> + 2002-02-11 Fred Fish <fnf@redhat.com>
> +
> + * dbxread.c (process_one_symbol): When finding an N_FUN symbol
> + that marks the end of the range of a function, enter a line number
> + entry that has a line number of zero and a PC offset that matches
> + the end of the function. This starts a range of PC's for which no
> + line number information is known.
> + * symtab.c (find_pc_sect_line): If our best fit is in a range of
> + PC's for which no line number info is found (line number is zero)
> + then we didn't find any valid line information.
> +
> 2002-02-11 Richard Earnshaw <rearnsha@arm.com>
>
> * arm-linux-nat.c: Really include arm-tdep.h.
> Index: dbxread.c
> ===================================================================
> RCS file: /cvs/src/src/gdb/dbxread.c,v
> retrieving revision 1.29
> diff -c -p -r1.29 dbxread.c
> *** dbxread.c 2002/02/04 11:55:34 1.29
> --- dbxread.c 2002/02/13 00:22:50
> *************** process_one_symbol (int type, int desc,
> *** 2741,2746 ****
> --- 2741,2747 ----
> {
> /* This N_FUN marks the end of a function. This closes off the
> current block. */
> + record_line (current_subfile, 0, function_start_offset + valu);
> within_function = 0;
> new = pop_context ();
>
> Index: symtab.c
> ===================================================================
> RCS file: /cvs/src/src/gdb/symtab.c,v
> retrieving revision 1.54
> diff -c -p -r1.54 symtab.c
> *** symtab.c 2002/02/11 03:21:53 1.54
> --- symtab.c 2002/02/13 00:22:54
> *************** find_pc_sect_line (CORE_ADDR pc, struct
> *** 1823,1828 ****
> --- 1823,1835 ----
> val.end = alt->pc;
> }
> }
> + else if (best->line == 0)
> + {
> + /* If our best fit is in a range of PC's for which no line
> + number info is available (line number is zero) then we didn't
> + find any valid line information. */
> + val.pc = pc;
> + }
> else
> {
> val.symtab = best_symtab;