Re: Single stepping a simple C-program, but...

Peter Toft
2008-05-20T07:11:59Z

On Mon, 19 May 2008 23:39:20 +0100, Pedro Alves <pedro@codesourcery.com>
wrote:
> A Monday 19 May 2008 21:48:18, Pedro Alves wrote:
>> A Monday 19 May 2008 21:18:36, Peter Toft wrote:
>> > Hi guys
>> >
>> > I was a bit surprised today with GDB, and I hope one of you can
> explain
>> > it to me. Take a look at http://pastebin.org/37117
>>
>> 01. #include <stdio.h>
>> 02.
>> 03. int main(void)
>> 04. {
>> 05. int ii=4;
>> 06.
>> 07. if ((ii>3) || (ii<1))
>> 08. printf("hej A\n");
>> 09. else
>> 10. printf("hej B\n");
>> 11.
>> 12. return 0;
>> 13. }
>>
>> > Press download and save as my_program.c
>> > $ gcc -g my_program.c
>> > $ gdb ./a.out
>> > (gdb) br 7
>> > Breakpoint 1 at 0x804838c: file my_program.c, line 7.
>> > (gdb) r
>> > Starting program: /home/pto/c/a.out
>> >
>> > Breakpoint 1, main () at my_program.c:7
>> > 7 if ((ii>3) || (ii<1))
>> > (gdb) s
>> > 8 printf("hej A\n");
>> > (gdb) s
>> > hej A
>> > 7 if ((ii>3) || (ii<1)) <----------- WHY!!!!??
>> > (gdb) s
>> > 12 return 0;
>> >
>> > -------
>> >
>> > Why does the second "step" i.e. "s" take me BACK to line 7 after I
>> > have been in line 8????
>>
>> Presumably, because there's a branch instruction after the printf
>> call to skip the else clause, GDB hits it (step-resume breakpoint),
>> and the debug info gcc is producing is marking that address as belonging
>> to line 7?
>
> I got curious.
> gcc version 4.2.3 (Ubuntu 4.2.3-2ubuntu7) (x86_64-linux-gnu)
> GNU gdb 6.8.50.20080515-cvs
>
> Built with gcc my_program.c -g3 -O0 -o my_program
>
> 5 int ii=4;
> (gdb) n
> 7 if ((ii>3) || (ii<1))
> (gdb)
> 8 printf("hej A\n");
> (gdb)
> hej A
> 7 if ((ii>3) || (ii<1))
> (gdb) p $pc
> $1 = (void (*)()) 0x4004bd <main+37>
>
> (gdb) disassemble /m
> 6
> 7 if ((ii>3) || (ii<1))
> 0x00000000004004a7 <main+15>: cmpl $0x3,-0x4(%rbp)
> 0x00000000004004ab <main+19>: jg 0x4004b3 <main+27>
> 0x00000000004004ad <main+21>: cmpl $0x0,-0x4(%rbp)
> 0x00000000004004b1 <main+25>: jg 0x4004bf <main+39>
> 0x00000000004004bd <main+37>: jmp 0x4004c9 <main+49>
>
> 8 printf("hej A\n");
> 0x00000000004004b3 <main+27>: mov $0x4005b8,%edi
> 0x00000000004004b8 <main+32>: callq 0x4003c0 <puts@plt>
>
> 9 else
> 10 printf("hej B\n");
> 0x00000000004004bf <main+39>: mov $0x4005be,%edi
> 0x00000000004004c4 <main+44>: callq 0x4003c0 <puts@plt>
>
> 11
> 12 return 0;
> 0x00000000004004c9 <main+49>: mov $0x0,%eax
>
> Humm, this output isn't clear, as GDB is grouping insns by
> line number first then sorting by address.
>
> objdump -dS:
>
> int main(void)
> {
> 400498: 55 push %rbp
> 400499: 48 89 e5 mov %rsp,%rbp
> 40049c: 48 83 ec 10 sub $0x10,%rsp
> int ii=4;
> 4004a0: c7 45 fc 04 00 00 00 movl $0x4,-0x4(%rbp)
>
> if ((ii>3) || (ii<1))
> 4004a7: 83 7d fc 03 cmpl $0x3,-0x4(%rbp)
> 4004ab: 7f 06 jg 4004b3 <main+0x1b>
> 4004ad: 83 7d fc 00 cmpl $0x0,-0x4(%rbp)
> 4004b1: 7f 0c jg 4004bf <main+0x27>
> printf("hej A\n");
> 4004b3: bf b8 05 40 00 mov $0x4005b8,%edi
> 4004b8: e8 03 ff ff ff callq 4003c0 <puts@plt>
>
> int main(void)
> {
> int ii=4;
>
> if ((ii>3) || (ii<1))
> 4004a7: 83 7d fc 03 cmpl $0x3,-0x4(%rbp)
> 4004ab: 7f 06 jg 4004b3 <main+0x1b>
> 4004ad: 83 7d fc 00 cmpl $0x0,-0x4(%rbp)
> 4004b1: 7f 0c jg 4004bf <main+0x27>
> printf("hej A\n");
> 4004b3: bf b8 05 40 00 mov $0x4005b8,%edi
> 4004b8: e8 03 ff ff ff callq 4003c0 <puts@plt>
>
> int main(void)
> {
> int ii=4;
>
> if ((ii>3) || (ii<1))
> 4004bd: eb 0a jmp 4004c9 <main+0x31>
>
> ^^^ jmp over else
> printf("hej A\n");
> else
> printf("hej B\n");
> 4004bf: bf be 05 40 00 mov $0x4005be,%edi
> 4004c4: e8 f7 fe ff ff callq 4003c0 <puts@plt>
>
> return 0;
> 4004c9: b8 00 00 00 00 mov $0x0,%eax
> }
>
> Ah, that's better. Notice how line info is making objdump print
> the same sources more than once:
>
> readelf -wl ./my_program
>
> Line Number Statements:
> Extended opcode 2: set Address to 0x400498
> Special opcode 8: advance Address by 0 to 0x400498 and Line by 3 to 4
> Special opcode 118: advance Address by 8 to 0x4004a0 and Line by 1 to 5
> Special opcode 105: advance Address by 7 to 0x4004a7 and Line by 2 to 7
> Special opcode 174: advance Address by 12 to 0x4004b3 and Line by 1 to
8
> Special opcode 144: advance Address by 10 to 0x4004bd and Line by -1 to
> 7
> ^^^^^^
> ^
> Special opcode 36: advance Address by 2 to 0x4004bf and Line by 3 to 10
> Special opcode 147: advance Address by 10 to 0x4004c9 and Line by 2 to
> 12
> Special opcode 76: advance Address by 5 to 0x4004ce and Line by 1 to 13
> Advance PC by 2 to 0x4004d0
> Extended opcode 1: End of Sequence
>
> There you go. Not much for GDB to do.
>
> --
> Pedro Alves

Pedro - your mail is very interesting and very illustrative.
To be 100% sure that I understand you, and "problem" that we get a line
counter increment of -1 from 8 to 7 above
comes from gcc i.e. gdb does the right thing. Do we agree?

Then the question is why gcc does this?!

Best

--
Peter Toft, Ph.D. [pto@linuxbog.dk] http://petertoft.dk
FÃ¸lg min Linux-blog pÃ¥ http://www.version2.dk/blogs/petertoft