I have a C++ function which has many return statements at various places. How to set a breakpoint at the return statement where the function actually returns ?
And what does "break" command without argument means?
Contrary to answers so far, most compilers will create a single return assembly instruction, regardless of how many return statements are in the function (it is convenient for the compiler to do that, so there is only a single place to perform all the stack frame cleanup).
If you wanted to stop on that instruction, all you have to do is disas and look for retq (or whatever the return instruction for your processor is), and set a breakpoint on it. For example:
int foo(int x)
{
switch(x) {
case 1: return 2;
case 2: return 3;
default: return 42;
}
}
int main()
{
return foo(0);
}
(gdb) disas foo
Dump of assembler code for function foo:
0x0000000000400448 <+0>: push %rbp
0x0000000000400449 <+1>: mov %rsp,%rbp
0x000000000040044c <+4>: mov %edi,-0x4(%rbp)
0x000000000040044f <+7>: mov -0x4(%rbp),%eax
0x0000000000400452 <+10>: mov %eax,-0xc(%rbp)
0x0000000000400455 <+13>: cmpl $0x1,-0xc(%rbp)
0x0000000000400459 <+17>: je 0x400463 <foo+27>
0x000000000040045b <+19>: cmpl $0x2,-0xc(%rbp)
0x000000000040045f <+23>: je 0x40046c <foo+36>
0x0000000000400461 <+25>: jmp 0x400475 <foo+45>
0x0000000000400463 <+27>: movl $0x2,-0x8(%rbp)
0x000000000040046a <+34>: jmp 0x40047c <foo+52>
0x000000000040046c <+36>: movl $0x3,-0x8(%rbp)
0x0000000000400473 <+43>: jmp 0x40047c <foo+52>
0x0000000000400475 <+45>: movl $0x2a,-0x8(%rbp)
0x000000000040047c <+52>: mov -0x8(%rbp),%eax
0x000000000040047f <+55>: leaveq
0x0000000000400480 <+56>: retq
End of assembler dump.
(gdb) b *0x0000000000400480
Breakpoint 1 at 0x400480
(gdb) r
Breakpoint 1, 0x0000000000400480 in foo ()
(gdb) p $rax
$1 = 42
You can use reverse debugging to find out where function actually returns. Finish executing current frame, do reverse-step and then you should stop at just returned statement.
(gdb) record
(gdb) fin
(gdb) reverse-step
Break on all retq of current function
This Python command puts a breakpoint on every retq instruction of the current function:
class BreakReturn(gdb.Command):
def __init__(self):
super().__init__(
'break-return',
gdb.COMMAND_RUNNING,
gdb.COMPLETE_NONE,
False
)
def invoke(self, arg, from_tty):
frame = gdb.selected_frame()
# TODO make this work if there is no debugging information, where .block() fails.
block = frame.block()
# Find the function block in case we are in an inner block.
while block:
if block.function:
break
block = block.superblock
start = block.start
end = block.end
arch = frame.architecture()
pc = gdb.selected_frame().pc()
instructions = arch.disassemble(start, end - 1)
for instruction in instructions:
if instruction['asm'].startswith('retq '):
gdb.Breakpoint('*{}'.format(instruction['addr']))
BreakReturn()
Source it with:
source gdb.py
and use the command as:
break-return
continue
You should now be at retq.
Step until retq
Just for fun, another implementation that stops when a retq is found (less efficient of because no hardware support):
class ContinueReturn(gdb.Command):
def __init__(self):
super().__init__(
'continue-return',
gdb.COMMAND_RUNNING,
gdb.COMPLETE_NONE,
False
)
def invoke(self, arg, from_tty):
thread = gdb.inferiors()[0].threads()[0]
while thread.is_valid():
gdb.execute('ni', to_string=True)
frame = gdb.selected_frame()
arch = frame.architecture()
pc = gdb.selected_frame().pc()
instruction = arch.disassemble(pc)[0]['asm']
if instruction.startswith('retq '):
break
ContinueReturn()
This will ignore your other breakpoints. TODO: can be avoided?
Not sure if it is faster or slower than reverse-step.
A version that stops at a given opcode can be found at: https://stackoverflow.com/a/31249378/895245
break without arguments stops execution at the next instruction in the currently selected stack frame. You select strack frames via the frame or up and down commands. If you want to debug the point where you are actually leaving the current function, select the next outer frame and break there.
rr reverse debugging
Similar to GDB record mentioned at https://stackoverflow.com/a/3649698/895245 , but much more functional as of GDB 7.11 vs rr 4.1.0 in Ubuntu 16.04.
Notably, it deals with AVX correctly:
gdb reverse debugging fails with "Process record does not support instruction 0xf0d at address"
"target record-full" in gdb makes "n" command fail on printf with "Process record does not support instruction 0xc5 at address 0x7ffff7dee6e7"?
which prevents it from working with the default standard library calls.
Install Ubuntu 16.04:
sudo apt-get install rr linux-tools-common linux-tools-generic linux-cloud-tools-generic
sudo cpupower frequency-set -g performance
But also consider compiling from source to get the latest updates, it was not hard.
Test program:
int where_return(int i) {
if (i)
return 1;
else
return 0;
}
int main(void) {
where_return(0);
where_return(1);
}
compile and run:
gcc -O0 -ggdb3 -o reverse.out -std=c89 -Wextra reverse.c
rr record ./reverse.out
rr replay
Now you are left inside a GDB session, and you can properly reverse debug:
(rr) break main
Breakpoint 1 at 0x56057c458619: file a.c, line 9.
(rr) continue
Continuing.
Breakpoint 1, main () at a.c:9
9 where_return(0);
(rr) step
where_return (i=0) at a.c:2
2 if (i)
(rr) finish
Run till exit from #0 where_return (i=0) at a.c:2
main () at a.c:10
10 where_return(1);
Value returned is $1 = 0
(rr) reverse-step
where_return (i=0) at a.c:6
6 }
(rr) reverse-step
5 return 0;
We are now on the correct return line.
If you can change the source code, you might use some dirty trick with the preprocessor:
void on_return() {
}
#define return return on_return(), /* If the function has a return value != void */
#define return return on_return() /* If the function has a return value == void */
/* <<<-- Insert your function here -->>> */
#undef return
Then set a breakpoint to on_return and go one frame up.
Attention: This will not work, if a function does not return via a return statement. So ensure, that it's last line is a return.
Example (shamelessly copied from C code, but will work also in C++):
#include <stdio.h>
/* Dummy function to place the breakpoint */
void on_return(void) {
}
#define return return on_return()
void myfun1(int a) {
if (a > 10) return;
printf("<10\n");
return;
}
#undef return
#define return return on_return(),
int myfun2(int a) {
if (a < 0) return -1;
if (a > 0) return 1;
return 0;
}
#undef return
int main(void)
{
myfun1(1);
myfun2(2);
}
The first macro will change
return;
to
return on_return();
Which is valid, since on_return also returns void.
The second macro will change
return -1;
to
return on_return(), -1;
Which will call on_return() and then return -1 (thanks to the ,-operator).
This is a very dirty trick, but despite using backwards-stepping, it will work in multi-threaded environments and inlined functions, too.
Break without argument sets a breakpoint at the current line.
There is no way for a single breakpoint to catch all return paths. Either set a breakpoint at the caller immediately after it returns, or break at all return statements.
Since this is C++, I suppose you could create a local sentry object, and break on its destructor, though.
Related
I am trying to debug C++ code. But when I set a breakpoint at the line "break;", the breakpoint cannot be hit. I am using MinGW64 to compile C++ code with -g flag on Windows, and without any optimization flags.
A short code to reproduce the problem:
#include <iostream>
#include <random>
using namespace std;
static default_random_engine generator(0); //random seed
static normal_distribution<double> normal;
static uniform_real_distribution<double> uniform;
static exponential_distribution<double> expo;
int main() {
int k_max = round(uniform(generator)*100+3);
double c = 0;
for (int k=0;k<k_max;k++) {
double a = expo(generator);
double b = normal(generator);
if ( a*b<a+b) {
c *= 1.5;
if (sqrt(a+1) < 0.7) {
c += 2*expo(generator);
} else if (sqrt(a+2)>2.9) {
c += 3*expo(generator);
} else {
c += expo(generator);
}
//std::cout<<"Before break"<<std::endl;
break; // Where the breakpoint is set
}
}
cout << c << endl;
}
What I have tried:
I disassembled the code before and after the breakpoint and saw something like that:
25 } else {
26 c += expo(generator);
0x0000000140001606 <+438>: lea 0x6a37(%rip),%rax # 0x140008044 <_ZL9generator>
0x000000014000160d <+445>: mov %rax,%rdx
0x0000000140001610 <+448>: lea 0x6a79(%rip),%rax # 0x140008090 <_ZL4expo>
0x0000000140001617 <+455>: mov %rax,%rcx
0x000000014000161a <+458>: call 0x140003180 <_ZNSt24exponential_distributionIdEclISt26linear_congruential_engineIjLj16807ELj0ELj2147483647EEEEdRT_>
0x000000014000161f <+463>: movsd -0x8(%rbp),%xmm1
0x0000000140001624 <+468>: addsd %xmm1,%xmm0
0x0000000140001628 <+472>: movsd %xmm0,-0x8(%rbp)
27 }
28 //std::cout<<"Before break"<<std::endl;
29 break;
0x0000000140001593 <+323>: jmp 0x14000163f <main()+495>
0x0000000140001604 <+436>: jmp 0x14000163f <main()+495>
0x000000014000162d <+477>: jmp 0x14000163f <main()+495>
30 }
So I wonder if the GDB debugger only insert INT 3 into one of the jump commands. If so, how can I ensure that the breakpoint is not skipped? (i.e. change the compilation flags.) Or, did I do something wrong when compiling and debugging?
I am debugging C++ in gdb 7.1 on Linux.
I have a function a() that is called in many places in the code. I want to set a breakpoint in it, but only if it was called from b(). Is there any way to do it?
Is there any way to do it only if b() was called from c(), and so on ad infinitum?
Update: There is now a better answer to this question: use GDB _is_caller convenience function.
The need you describe comes up quite often, usually in the context of some_utility_fn being called a lot, but you only are interested in the call which comes from some_other_fn.
You could probably script this entire interaction using the new embedded Python support in GDB from CVS trunk.
Without Python, you are limited in what you can do, but the usual technique is to have a disabled breakpoint on a(), and enable it from a command, attached to a breakpoint on b().
Here is an example:
int a(int x)
{
return x + 1;
}
int b()
{
return a(1);
}
int call_a_lots()
{
int i, sum = 0;
for (i = 0; i < 100; i++)
sum += a(i);
}
int main()
{
call_a_lots();
return b();
}
gcc -g t.c
gdb -q ./a.out
Reading symbols from /tmp/a.out...done.
(gdb) break a
Breakpoint 1 at 0x4004cb: file t.c, line 3.
(gdb) disable 1
(gdb) break b
Breakpoint 2 at 0x4004d7: file t.c, line 8.
(gdb) command 2
>silent
>enable 1
>continue
>end
(gdb) run
Breakpoint 1, a (x=1) at t.c:3
3 return x + 1;
(gdb) bt
#0 a (x=1) at t.c:3
#1 0x00000000004004e1 in b () at t.c:8
#2 0x000000000040052c in main () at t.c:21
(gdb) q
Voila: we've stopped on a() called from b(), ignoring previous 100 calls to a().
gdb can handle this directly now without any need for Python. Just do this:
b a if $_caller_is("b")
I have tested this on gdb 7.6 that is already available but it does not work on gdb 7.2 and probably on gdb 7.1:
So this is main.cpp:
int a()
{
int p = 0;
p = p +1;
return p;
}
int b()
{
return a();
}
int c()
{
return a();
}
int main()
{
c();
b();
a();
return 0;
}
Then g++ -g main.cpp
This is my_check.py:
class MyBreakpoint (gdb.Breakpoint):
def stop (self):
if gdb.selected_frame().older().name()=="b":
gdb.execute("bt")
return True
else:
return False
MyBreakpoint("a")
And this is how it works:
4>gdb -q -x my_check.py ./a.out
Reading symbols from /home/a.out...done.
Breakpoint 1 at 0x400540: file main.cpp, line 3.
(gdb) r
Starting program: /home/a.out
#0 a () at main.cpp:3
#1 0x0000000000400559 in b () at main.cpp:10
#2 0x0000000000400574 in main () at main.cpp:21
Breakpoint 1, a () at main.cpp:3
3 int p = 0;
(gdb) c
Continuing.
[Inferior 1 (process 16739) exited normally]
(gdb) quit
A simpler solution than Python scripting is using a temporary breakpoint.
It looks like this:
b ParentFunction
command 1
tb FunctionImInterestedIn
c
end
Every time you break in ParentFunction, you'll set a one-time breakpoint on the function you're actually interested in, then continue running (presumably until you hit that breakpoint).
Since you'll break exactly once on FunctionImInterestedIn, this won't work if FunctionImInterestedIn is called multiple times in the context of ParentFunction and you want to break on each invocation.
not sure how to do it by gdb.
But you can declare global variable like:
bool call_a = false;
and when b calling a
call_a = true;
a();
and set call_a to false when other function call a() or after your breakpoint
then use condition break-point
break [line-number] if call_a == true
An easy one for arm is:
Set the breakpoint in the function you are interested.
break a
Attach an gdb command to that breakpoint.
command 1
up 1
if $lr == 0x12345678
echo match \n
down 1
else
echo no match \n
echo $lr \n
down 1
cont
end
end
When ever you arrive in the function a(), the command temporarily pops up one stack frame thus updating the link register. The callers link register value can then be used continue when the caller is not the execution
path you need.
Enjoy.
I have a C++ function which has many return statements at various places. How to set a breakpoint at the return statement where the function actually returns ?
And what does "break" command without argument means?
Contrary to answers so far, most compilers will create a single return assembly instruction, regardless of how many return statements are in the function (it is convenient for the compiler to do that, so there is only a single place to perform all the stack frame cleanup).
If you wanted to stop on that instruction, all you have to do is disas and look for retq (or whatever the return instruction for your processor is), and set a breakpoint on it. For example:
int foo(int x)
{
switch(x) {
case 1: return 2;
case 2: return 3;
default: return 42;
}
}
int main()
{
return foo(0);
}
(gdb) disas foo
Dump of assembler code for function foo:
0x0000000000400448 <+0>: push %rbp
0x0000000000400449 <+1>: mov %rsp,%rbp
0x000000000040044c <+4>: mov %edi,-0x4(%rbp)
0x000000000040044f <+7>: mov -0x4(%rbp),%eax
0x0000000000400452 <+10>: mov %eax,-0xc(%rbp)
0x0000000000400455 <+13>: cmpl $0x1,-0xc(%rbp)
0x0000000000400459 <+17>: je 0x400463 <foo+27>
0x000000000040045b <+19>: cmpl $0x2,-0xc(%rbp)
0x000000000040045f <+23>: je 0x40046c <foo+36>
0x0000000000400461 <+25>: jmp 0x400475 <foo+45>
0x0000000000400463 <+27>: movl $0x2,-0x8(%rbp)
0x000000000040046a <+34>: jmp 0x40047c <foo+52>
0x000000000040046c <+36>: movl $0x3,-0x8(%rbp)
0x0000000000400473 <+43>: jmp 0x40047c <foo+52>
0x0000000000400475 <+45>: movl $0x2a,-0x8(%rbp)
0x000000000040047c <+52>: mov -0x8(%rbp),%eax
0x000000000040047f <+55>: leaveq
0x0000000000400480 <+56>: retq
End of assembler dump.
(gdb) b *0x0000000000400480
Breakpoint 1 at 0x400480
(gdb) r
Breakpoint 1, 0x0000000000400480 in foo ()
(gdb) p $rax
$1 = 42
You can use reverse debugging to find out where function actually returns. Finish executing current frame, do reverse-step and then you should stop at just returned statement.
(gdb) record
(gdb) fin
(gdb) reverse-step
Break on all retq of current function
This Python command puts a breakpoint on every retq instruction of the current function:
class BreakReturn(gdb.Command):
def __init__(self):
super().__init__(
'break-return',
gdb.COMMAND_RUNNING,
gdb.COMPLETE_NONE,
False
)
def invoke(self, arg, from_tty):
frame = gdb.selected_frame()
# TODO make this work if there is no debugging information, where .block() fails.
block = frame.block()
# Find the function block in case we are in an inner block.
while block:
if block.function:
break
block = block.superblock
start = block.start
end = block.end
arch = frame.architecture()
pc = gdb.selected_frame().pc()
instructions = arch.disassemble(start, end - 1)
for instruction in instructions:
if instruction['asm'].startswith('retq '):
gdb.Breakpoint('*{}'.format(instruction['addr']))
BreakReturn()
Source it with:
source gdb.py
and use the command as:
break-return
continue
You should now be at retq.
Step until retq
Just for fun, another implementation that stops when a retq is found (less efficient of because no hardware support):
class ContinueReturn(gdb.Command):
def __init__(self):
super().__init__(
'continue-return',
gdb.COMMAND_RUNNING,
gdb.COMPLETE_NONE,
False
)
def invoke(self, arg, from_tty):
thread = gdb.inferiors()[0].threads()[0]
while thread.is_valid():
gdb.execute('ni', to_string=True)
frame = gdb.selected_frame()
arch = frame.architecture()
pc = gdb.selected_frame().pc()
instruction = arch.disassemble(pc)[0]['asm']
if instruction.startswith('retq '):
break
ContinueReturn()
This will ignore your other breakpoints. TODO: can be avoided?
Not sure if it is faster or slower than reverse-step.
A version that stops at a given opcode can be found at: https://stackoverflow.com/a/31249378/895245
break without arguments stops execution at the next instruction in the currently selected stack frame. You select strack frames via the frame or up and down commands. If you want to debug the point where you are actually leaving the current function, select the next outer frame and break there.
rr reverse debugging
Similar to GDB record mentioned at https://stackoverflow.com/a/3649698/895245 , but much more functional as of GDB 7.11 vs rr 4.1.0 in Ubuntu 16.04.
Notably, it deals with AVX correctly:
gdb reverse debugging fails with "Process record does not support instruction 0xf0d at address"
"target record-full" in gdb makes "n" command fail on printf with "Process record does not support instruction 0xc5 at address 0x7ffff7dee6e7"?
which prevents it from working with the default standard library calls.
Install Ubuntu 16.04:
sudo apt-get install rr linux-tools-common linux-tools-generic linux-cloud-tools-generic
sudo cpupower frequency-set -g performance
But also consider compiling from source to get the latest updates, it was not hard.
Test program:
int where_return(int i) {
if (i)
return 1;
else
return 0;
}
int main(void) {
where_return(0);
where_return(1);
}
compile and run:
gcc -O0 -ggdb3 -o reverse.out -std=c89 -Wextra reverse.c
rr record ./reverse.out
rr replay
Now you are left inside a GDB session, and you can properly reverse debug:
(rr) break main
Breakpoint 1 at 0x56057c458619: file a.c, line 9.
(rr) continue
Continuing.
Breakpoint 1, main () at a.c:9
9 where_return(0);
(rr) step
where_return (i=0) at a.c:2
2 if (i)
(rr) finish
Run till exit from #0 where_return (i=0) at a.c:2
main () at a.c:10
10 where_return(1);
Value returned is $1 = 0
(rr) reverse-step
where_return (i=0) at a.c:6
6 }
(rr) reverse-step
5 return 0;
We are now on the correct return line.
If you can change the source code, you might use some dirty trick with the preprocessor:
void on_return() {
}
#define return return on_return(), /* If the function has a return value != void */
#define return return on_return() /* If the function has a return value == void */
/* <<<-- Insert your function here -->>> */
#undef return
Then set a breakpoint to on_return and go one frame up.
Attention: This will not work, if a function does not return via a return statement. So ensure, that it's last line is a return.
Example (shamelessly copied from C code, but will work also in C++):
#include <stdio.h>
/* Dummy function to place the breakpoint */
void on_return(void) {
}
#define return return on_return()
void myfun1(int a) {
if (a > 10) return;
printf("<10\n");
return;
}
#undef return
#define return return on_return(),
int myfun2(int a) {
if (a < 0) return -1;
if (a > 0) return 1;
return 0;
}
#undef return
int main(void)
{
myfun1(1);
myfun2(2);
}
The first macro will change
return;
to
return on_return();
Which is valid, since on_return also returns void.
The second macro will change
return -1;
to
return on_return(), -1;
Which will call on_return() and then return -1 (thanks to the ,-operator).
This is a very dirty trick, but despite using backwards-stepping, it will work in multi-threaded environments and inlined functions, too.
Break without argument sets a breakpoint at the current line.
There is no way for a single breakpoint to catch all return paths. Either set a breakpoint at the caller immediately after it returns, or break at all return statements.
Since this is C++, I suppose you could create a local sentry object, and break on its destructor, though.
I am debugging C++ in gdb 7.1 on Linux.
I have a function a() that is called in many places in the code. I want to set a breakpoint in it, but only if it was called from b(). Is there any way to do it?
Is there any way to do it only if b() was called from c(), and so on ad infinitum?
Update: There is now a better answer to this question: use GDB _is_caller convenience function.
The need you describe comes up quite often, usually in the context of some_utility_fn being called a lot, but you only are interested in the call which comes from some_other_fn.
You could probably script this entire interaction using the new embedded Python support in GDB from CVS trunk.
Without Python, you are limited in what you can do, but the usual technique is to have a disabled breakpoint on a(), and enable it from a command, attached to a breakpoint on b().
Here is an example:
int a(int x)
{
return x + 1;
}
int b()
{
return a(1);
}
int call_a_lots()
{
int i, sum = 0;
for (i = 0; i < 100; i++)
sum += a(i);
}
int main()
{
call_a_lots();
return b();
}
gcc -g t.c
gdb -q ./a.out
Reading symbols from /tmp/a.out...done.
(gdb) break a
Breakpoint 1 at 0x4004cb: file t.c, line 3.
(gdb) disable 1
(gdb) break b
Breakpoint 2 at 0x4004d7: file t.c, line 8.
(gdb) command 2
>silent
>enable 1
>continue
>end
(gdb) run
Breakpoint 1, a (x=1) at t.c:3
3 return x + 1;
(gdb) bt
#0 a (x=1) at t.c:3
#1 0x00000000004004e1 in b () at t.c:8
#2 0x000000000040052c in main () at t.c:21
(gdb) q
Voila: we've stopped on a() called from b(), ignoring previous 100 calls to a().
gdb can handle this directly now without any need for Python. Just do this:
b a if $_caller_is("b")
I have tested this on gdb 7.6 that is already available but it does not work on gdb 7.2 and probably on gdb 7.1:
So this is main.cpp:
int a()
{
int p = 0;
p = p +1;
return p;
}
int b()
{
return a();
}
int c()
{
return a();
}
int main()
{
c();
b();
a();
return 0;
}
Then g++ -g main.cpp
This is my_check.py:
class MyBreakpoint (gdb.Breakpoint):
def stop (self):
if gdb.selected_frame().older().name()=="b":
gdb.execute("bt")
return True
else:
return False
MyBreakpoint("a")
And this is how it works:
4>gdb -q -x my_check.py ./a.out
Reading symbols from /home/a.out...done.
Breakpoint 1 at 0x400540: file main.cpp, line 3.
(gdb) r
Starting program: /home/a.out
#0 a () at main.cpp:3
#1 0x0000000000400559 in b () at main.cpp:10
#2 0x0000000000400574 in main () at main.cpp:21
Breakpoint 1, a () at main.cpp:3
3 int p = 0;
(gdb) c
Continuing.
[Inferior 1 (process 16739) exited normally]
(gdb) quit
A simpler solution than Python scripting is using a temporary breakpoint.
It looks like this:
b ParentFunction
command 1
tb FunctionImInterestedIn
c
end
Every time you break in ParentFunction, you'll set a one-time breakpoint on the function you're actually interested in, then continue running (presumably until you hit that breakpoint).
Since you'll break exactly once on FunctionImInterestedIn, this won't work if FunctionImInterestedIn is called multiple times in the context of ParentFunction and you want to break on each invocation.
not sure how to do it by gdb.
But you can declare global variable like:
bool call_a = false;
and when b calling a
call_a = true;
a();
and set call_a to false when other function call a() or after your breakpoint
then use condition break-point
break [line-number] if call_a == true
An easy one for arm is:
Set the breakpoint in the function you are interested.
break a
Attach an gdb command to that breakpoint.
command 1
up 1
if $lr == 0x12345678
echo match \n
down 1
else
echo no match \n
echo $lr \n
down 1
cont
end
end
When ever you arrive in the function a(), the command temporarily pops up one stack frame thus updating the link register. The callers link register value can then be used continue when the caller is not the execution
path you need.
Enjoy.
I have a C++ function which has many return statements at various places. How to set a breakpoint at the return statement where the function actually returns ?
And what does "break" command without argument means?
Contrary to answers so far, most compilers will create a single return assembly instruction, regardless of how many return statements are in the function (it is convenient for the compiler to do that, so there is only a single place to perform all the stack frame cleanup).
If you wanted to stop on that instruction, all you have to do is disas and look for retq (or whatever the return instruction for your processor is), and set a breakpoint on it. For example:
int foo(int x)
{
switch(x) {
case 1: return 2;
case 2: return 3;
default: return 42;
}
}
int main()
{
return foo(0);
}
(gdb) disas foo
Dump of assembler code for function foo:
0x0000000000400448 <+0>: push %rbp
0x0000000000400449 <+1>: mov %rsp,%rbp
0x000000000040044c <+4>: mov %edi,-0x4(%rbp)
0x000000000040044f <+7>: mov -0x4(%rbp),%eax
0x0000000000400452 <+10>: mov %eax,-0xc(%rbp)
0x0000000000400455 <+13>: cmpl $0x1,-0xc(%rbp)
0x0000000000400459 <+17>: je 0x400463 <foo+27>
0x000000000040045b <+19>: cmpl $0x2,-0xc(%rbp)
0x000000000040045f <+23>: je 0x40046c <foo+36>
0x0000000000400461 <+25>: jmp 0x400475 <foo+45>
0x0000000000400463 <+27>: movl $0x2,-0x8(%rbp)
0x000000000040046a <+34>: jmp 0x40047c <foo+52>
0x000000000040046c <+36>: movl $0x3,-0x8(%rbp)
0x0000000000400473 <+43>: jmp 0x40047c <foo+52>
0x0000000000400475 <+45>: movl $0x2a,-0x8(%rbp)
0x000000000040047c <+52>: mov -0x8(%rbp),%eax
0x000000000040047f <+55>: leaveq
0x0000000000400480 <+56>: retq
End of assembler dump.
(gdb) b *0x0000000000400480
Breakpoint 1 at 0x400480
(gdb) r
Breakpoint 1, 0x0000000000400480 in foo ()
(gdb) p $rax
$1 = 42
You can use reverse debugging to find out where function actually returns. Finish executing current frame, do reverse-step and then you should stop at just returned statement.
(gdb) record
(gdb) fin
(gdb) reverse-step
Break on all retq of current function
This Python command puts a breakpoint on every retq instruction of the current function:
class BreakReturn(gdb.Command):
def __init__(self):
super().__init__(
'break-return',
gdb.COMMAND_RUNNING,
gdb.COMPLETE_NONE,
False
)
def invoke(self, arg, from_tty):
frame = gdb.selected_frame()
# TODO make this work if there is no debugging information, where .block() fails.
block = frame.block()
# Find the function block in case we are in an inner block.
while block:
if block.function:
break
block = block.superblock
start = block.start
end = block.end
arch = frame.architecture()
pc = gdb.selected_frame().pc()
instructions = arch.disassemble(start, end - 1)
for instruction in instructions:
if instruction['asm'].startswith('retq '):
gdb.Breakpoint('*{}'.format(instruction['addr']))
BreakReturn()
Source it with:
source gdb.py
and use the command as:
break-return
continue
You should now be at retq.
Step until retq
Just for fun, another implementation that stops when a retq is found (less efficient of because no hardware support):
class ContinueReturn(gdb.Command):
def __init__(self):
super().__init__(
'continue-return',
gdb.COMMAND_RUNNING,
gdb.COMPLETE_NONE,
False
)
def invoke(self, arg, from_tty):
thread = gdb.inferiors()[0].threads()[0]
while thread.is_valid():
gdb.execute('ni', to_string=True)
frame = gdb.selected_frame()
arch = frame.architecture()
pc = gdb.selected_frame().pc()
instruction = arch.disassemble(pc)[0]['asm']
if instruction.startswith('retq '):
break
ContinueReturn()
This will ignore your other breakpoints. TODO: can be avoided?
Not sure if it is faster or slower than reverse-step.
A version that stops at a given opcode can be found at: https://stackoverflow.com/a/31249378/895245
break without arguments stops execution at the next instruction in the currently selected stack frame. You select strack frames via the frame or up and down commands. If you want to debug the point where you are actually leaving the current function, select the next outer frame and break there.
rr reverse debugging
Similar to GDB record mentioned at https://stackoverflow.com/a/3649698/895245 , but much more functional as of GDB 7.11 vs rr 4.1.0 in Ubuntu 16.04.
Notably, it deals with AVX correctly:
gdb reverse debugging fails with "Process record does not support instruction 0xf0d at address"
"target record-full" in gdb makes "n" command fail on printf with "Process record does not support instruction 0xc5 at address 0x7ffff7dee6e7"?
which prevents it from working with the default standard library calls.
Install Ubuntu 16.04:
sudo apt-get install rr linux-tools-common linux-tools-generic linux-cloud-tools-generic
sudo cpupower frequency-set -g performance
But also consider compiling from source to get the latest updates, it was not hard.
Test program:
int where_return(int i) {
if (i)
return 1;
else
return 0;
}
int main(void) {
where_return(0);
where_return(1);
}
compile and run:
gcc -O0 -ggdb3 -o reverse.out -std=c89 -Wextra reverse.c
rr record ./reverse.out
rr replay
Now you are left inside a GDB session, and you can properly reverse debug:
(rr) break main
Breakpoint 1 at 0x56057c458619: file a.c, line 9.
(rr) continue
Continuing.
Breakpoint 1, main () at a.c:9
9 where_return(0);
(rr) step
where_return (i=0) at a.c:2
2 if (i)
(rr) finish
Run till exit from #0 where_return (i=0) at a.c:2
main () at a.c:10
10 where_return(1);
Value returned is $1 = 0
(rr) reverse-step
where_return (i=0) at a.c:6
6 }
(rr) reverse-step
5 return 0;
We are now on the correct return line.
If you can change the source code, you might use some dirty trick with the preprocessor:
void on_return() {
}
#define return return on_return(), /* If the function has a return value != void */
#define return return on_return() /* If the function has a return value == void */
/* <<<-- Insert your function here -->>> */
#undef return
Then set a breakpoint to on_return and go one frame up.
Attention: This will not work, if a function does not return via a return statement. So ensure, that it's last line is a return.
Example (shamelessly copied from C code, but will work also in C++):
#include <stdio.h>
/* Dummy function to place the breakpoint */
void on_return(void) {
}
#define return return on_return()
void myfun1(int a) {
if (a > 10) return;
printf("<10\n");
return;
}
#undef return
#define return return on_return(),
int myfun2(int a) {
if (a < 0) return -1;
if (a > 0) return 1;
return 0;
}
#undef return
int main(void)
{
myfun1(1);
myfun2(2);
}
The first macro will change
return;
to
return on_return();
Which is valid, since on_return also returns void.
The second macro will change
return -1;
to
return on_return(), -1;
Which will call on_return() and then return -1 (thanks to the ,-operator).
This is a very dirty trick, but despite using backwards-stepping, it will work in multi-threaded environments and inlined functions, too.
Break without argument sets a breakpoint at the current line.
There is no way for a single breakpoint to catch all return paths. Either set a breakpoint at the caller immediately after it returns, or break at all return statements.
Since this is C++, I suppose you could create a local sentry object, and break on its destructor, though.