maniac_inside
maniac_inside

Reputation: 988

Watchpoint a fixed address

For my current embedded application I am trying to put GDB watch point at a fixed memory address.

As an example, my application updates the following address: 0x10793ad0. In order to be sure which part of the code is corrupting the value, I tried

watch 0x10793ad0

Even though GDB does not print any error after this, it is not able to break during execution even though I verified the value is getting modified at between start and end of execution.

Questions:

  1. Can I really put watch at a fixed address? I didn't come across any such example online.
  2. Is this the right way or am I missing something?

Upvotes: 82

Views: 67155

Answers (2)

ks1322
ks1322

Reputation: 35716

The right way to set watchpoint on address is watch *0x10793ad0. See gdb doc.

You can also specify what type GDB should interpret the memory address as. For example:

watch *(unsigned char*)0x10793ad0

or

watch *(int*)0x10793ad0

Upvotes: 124

-l/-location example

In this answer I'd like to further highlight the -l/-location option previously mentioned by Nathan Kidd in a comment.

If you don't use this option, then doing:

watch mylocalvar

makes GDB break as soon as mylocalvar goes out of scope, and then automatically delete that memory watchpoint and continue execution.

-l can therefore be very useful when you know that there is just one instance of some value being passed around by reference, typically a this-like struct.

Consider:

main.c

typedef struct {
    int i;
} MyThing;

void MyThing_init(MyThing *s, int i) {
    i += 1;
    s->i = i;
    i += 2;
    i += 3;
}

int MyThing_get_i(MyThing *s) {
    return s->i;
}

int main(void) {
    MyThing s;
    MyThing_init(&s, 1);
    MyThing_init(&s, 2);
    MyThing_init(&s, 3);
    MyThing_init(&s, 4);
    return MyThing_get_i(&s);
}

Compile and debug:

gcc -ggdb3 -O0 -o main.out main.c
gdb -nh -ex 'tb MyThing_init' -ex r main.out

This leaves as at:

6           i += 1;

Now if we do:

watch s->i

it shows up under:

(gdb) i b
Num     Type           Disp Enb Address            What
2       hw watchpoint  keep y                      s->i

then the first c stops just after the variable is modified as desired:

(gdb) c
Continuing.

Hardware watchpoint 2: s->i

Old value = 0
New value = 2
MyThing_init (s=0x7fffffffccb4, i=2) at main.c:8
8           i += 2;

but the second c stops when MyThing_init returns and explains to us that it deleted the watchpoint:

(gdb) c
Continuing.

Watchpoint 2 deleted because the program has left the block in
which its expression is valid.
main () at main.c:19
19          MyThing_init(&s, 2);

which we can confirm with:

(gdb) i b
No breakpoints or watchpoints.

and so running the program with c again will just run until it exits.

But if we instead use -l:

watch -l s->i

which shows up under i b as:

(gdb) i b
Num     Type           Disp Enb Address            What
2       hw watchpoint  keep y                      -location s->i

then the watchpoint does not get deleted, and we break on every call:


(gdb) c
Continuing.

Hardware watchpoint 2: -location s->i

Old value = 2
New value = 3
MyThing_init (s=0x7fffffffccb4, i=3) at main.c:8
8           i += 2;
(gdb) c
Continuing.

Hardware watchpoint 2: -location s->i

Old value = 3
New value = 4
MyThing_init (s=0x7fffffffccb4, i=4) at main.c:8
8           i += 2;
(gdb) c
Continuing.

Hardware watchpoint 2: -location s->i

Old value = 4
New value = 5
MyThing_init (s=0x7fffffffccb4, i=5) at main.c:8
8           i += 2;
(gdb) c
Continuing.

Hardware watchpoint 2: -location s->i

Old value = 5
New value = 32767
0x00007ffff7c44fd6 in __run_exit_handlers (status=5, listp=0x7ffff7dfe860 <__exit_funcs>, run_list_atexit=run_list_atexit@entry=true, run_dtors=run_dtors@entry=true) at ./stdlib/exit.c:43
43      ./stdlib/exit.c: No such file or directory.

This technique is particularly powerful in conjunction with Mozilla rr reverse debugging, because then what usually happens is you are at a point of failure like:

b MyThing_get_i

and you notice a bad value for something, say s->i, and then you want to know what was the last thing that set that bad value. Once in rr, you can immediately answer that with:

watch -l s->i
rc

It is a thing of great beauty.

Tested on Ubuntu 23.10, GCC 13.2.0. GDB 14.

Upvotes: 5

Related Questions