dcmp
dcmp

Reputation: 33

Using GDB to change a for loop condition?

I am trying to debug a program and am interested in the line for(int i =0; i<10; i++) and want to change i <10 to i<=10 in the GDB debugger. I've used print to change variable names, but how do I do it for this? Thank you.

Upvotes: 3

Views: 1597

Answers (1)

Employed Russian
Employed Russian

Reputation: 213799

want to change i <10 to i<=10 in the GDB debugger.

There are few ways to do this, depending on exactly what you need.

I'll assume that you simply need to do this once, that your binary is built without optimizations, on x86_64.

Given:

#include <stdio.h>
int main()
{
  for (int i = 0; i < 10; i++)
    printf("%d\n", i);
  return 0;
}

gcc -g -std=c99 t.c && gdb -q ./a.out

gdb) disas main
Dump of assembler code for function main:
   0x000000000040052d <+0>:     push   %rbp
   0x000000000040052e <+1>:     mov    %rsp,%rbp
   0x0000000000400531 <+4>:     sub    $0x10,%rsp
   0x0000000000400535 <+8>:     movl   $0x0,-0x4(%rbp)
   0x000000000040053c <+15>:    jmp    0x400556 <main+41>
   0x000000000040053e <+17>:    mov    -0x4(%rbp),%eax
   0x0000000000400541 <+20>:    mov    %eax,%esi
   0x0000000000400543 <+22>:    mov    $0x4005f4,%edi
   0x0000000000400548 <+27>:    mov    $0x0,%eax
   0x000000000040054d <+32>:    callq  0x400410 <printf@plt>
   0x0000000000400552 <+37>:    addl   $0x1,-0x4(%rbp)
   0x0000000000400556 <+41>:    cmpl   $0x9,-0x4(%rbp)
   0x000000000040055a <+45>:    jle    0x40053e <main+17>
   0x000000000040055c <+47>:    mov    $0x0,%eax
   0x0000000000400561 <+52>:    leaveq 
   0x0000000000400562 <+53>:    retq   
End of assembler dump.

Here you can see that the instruction at address 0x400556 compares value of i (stored on stack in location $rbp-4) with constant 9, and jumps back if the value is less than or equal to 9.

So you can set a breakpoint on instruction at 0x40055a, and then force the jump to be taken even when the compiled code says it shouldn't be:

(gdb) b *0x40055a if i == 10
Breakpoint 1 at 0x40055a: file t.c, line 4.
(gdb) run
Starting program: /tmp/a.out 
0
1
2
3
4
5
6
7
8
9

Breakpoint 1, 0x000000000040055a in main () at t.c:4
4             for (int i = 0; i < 10; i++)
(gdb) p i
$1 = 10
(gdb) jump *0x40053e
Continuing at 0x40053e.
10
[Inferior 1 (process 22210) exited normally]

Voila: we've printed an extra value.

Another possible approach: set a breakpoint on instruction at 0x400556, adjust the value of i to i-1, single-step, adjust the value of i to i+1, continue.

Yet another approach: binary patch the instruction at 0x400556 to compare with constant 10 instead of 9:

(gdb) disas/r 0x400556,0x400557
Dump of assembler code from 0x400556 to 0x400557:
   0x0000000000400556 <main+41>:        83 7d fc 09     cmpl   $0x9,-0x4(%rbp)
End of assembler dump.

Here you can see that the constant 9 is part of the instruction bytes, in particular the byte at address 0x400559. You can change that byte:

(gdb) start

Starting program: /tmp/a.out 

Temporary breakpoint 1, main () at t.c:4
4             for (int i = 0; i < 10; i++)

Let's overwrite the instruction and disassemble again:

(gdb) set *(char*)0x400559 = 10
(gdb) disas/r 0x400556,0x400557
Dump of assembler code from 0x400556 to 0x400557:
   0x0000000000400556 <main+41>:        83 7d fc 0a     cmpl   $0xa,-0x4(%rbp)
End of assembler dump.

Looks good: we now compare to 10 instead of 9. Does it work?

(gdb) c
Continuing.
0
1
2
3
4
5
6
7
8
9
10
[Inferior 1 (process 23131) exited normally]

Yes, it does!

P.S. Binary-patching the instruction is equivalent to editing the source and rebuilding the binary, except the patch is "forgotten" on next run.

Upvotes: 3

Related Questions