Files
binutils-gdb/gdb/testsuite/gdb.base/frame-selection.c
Andrew Burgess f0848033b8 gdb: fix selecting tail-call frames by name
I noticed that attempting to select a tail-call frame using 'frame
function NAME' wouldn't work:

  (gdb) bt
  #0  func_that_never_returns () at /tmp/build/gdb/testsuite/../../../src/gdb/testsuite/gdb.base/frame-selection.c:49
  #1  0x0000000000401183 in func_that_tail_calls () at /tmp/build/gdb/testsuite/../../../src/gdb/testsuite/gdb.base/frame-selection.c:59
  #2  0x00000000004011a5 in main () at /tmp/build/gdb/testsuite/../../../src/gdb/testsuite/gdb.base/frame-selection.c:70
  (gdb) frame function func_that_tail_calls
  No frame for function "func_that_tail_calls".
  (gdb) up
  #1  0x0000000000401183 in func_that_tail_calls () at /tmp/build/gdb/testsuite/../../../src/gdb/testsuite/gdb.base/frame-selection.c:59
  59	  func_that_never_returns ();
  (gdb) disassemble
  Dump of assembler code for function func_that_tail_calls:
     0x000000000040117a <+0>:	push   %rbp
     0x000000000040117b <+1>:	mov    %rsp,%rbp
     0x000000000040117e <+4>:	call   0x40116c <func_that_never_returns>
  End of assembler dump.
  (gdb)

The problem is that the 'function' mechanism uses get_frame_pc() and
then compares the address returned with the bounds of the function
we're looking for.

So in this case, the bounds of func_that_tail_calls are 0x40117a to
0x401183, with 0x401183 being the first address _after_ the function.

However, because func_that_tail_calls ends in a tail call, then the
get_frame_pc() is 0x401183, the first address after the function.  As
a result, GDB fails to realise that frame #1 is inside the function
we're looking for, and the lookup fails.

The fix is to use get_frame_address_in_block, which will return an
adjusted address, in this case, 0x401182, which is within the function
bounds.  Now the lookup works:

  (gdb) frame function func_that_tail_calls
  #1  0x0000000000401183 in func_that_tail_calls () at /tmp/build/gdb/testsuite/../../../src/gdb/testsuite/gdb.base/frame-selection.c:59
  59	  func_that_never_returns ();
  (gdb)

I've extended the gdb.base/frame-selection.exp test to cover this
case.
2025-02-10 10:06:06 +00:00

74 lines
1.4 KiB
C

/* Copyright 2018-2024 Free Software Foundation, Inc.
This file is part of GDB.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include <stdlib.h>
int
frame_2 (void)
{
return 0;
}
int
frame_1 (void)
{
return frame_2 ();
}
int
recursive (int arg)
{
int v;
if (arg < 2)
v = recursive (arg + 1);
else
v = frame_2 ();
return v;
}
/* A function that never returns. */
void __attribute__((noreturn))
func_that_never_returns (void)
{
exit (0);
}
/* A function that tail calls. Calling a 'noreturn' function isn't
required for a tail call, but at low optimisation levels, gcc will apply
the tail call optimisation only for 'noreturn' calls. */
void
func_that_tail_calls (void)
{
func_that_never_returns ();
}
int
main (void)
{
int i, j;
i = frame_1 ();
j = recursive (0);
func_that_tail_calls ();
return i + j;
}