[PATCH v3 3/4] gdb/infrun: handle stepping through functions with DW_AT_trampoline
Bruno Larsen
blarsen@redhat.com
Thu Jul 27 11:46:42 GMT 2023
On 11/07/2023 00:56, Abdul Basit Ijaz via Gdb-patches wrote:
> From: Nils-Christian Kempke <nils-christian.kempke@intel.com>
>
> This patch makes infrun continue stepping into and through trampoline
> functions marked via the DW_AT_trampoline in DWARF. The attribute can
> be emitted by the compiler for certain subroutines/inlined subroutines
> that are compiler generated and should be hidden from a user.
>
> Mainly, infrun is modified in 3 ways.
>
> First, GDB will now attempt to step through trampoline functions.
> Whenever we issued a step command that would make GDB step into a
> function that is marked trampoline, GDB will try to step directly towards
> the trampoline's 'target' instead and, e.g., not stop at the first
> instruction of the trampoline.
> The target can be specified by the compiler by the value of
> DW_AT_trampoline if its form is either an address, a name, or a DIE
> reference. DW_AT_trampoline is also allowed to be specified as a flag
> (containing true or false), in which case the target is assumed to be
> unknown. If GDB successfully finds a target, so if the value of
> DW_AT_trampoline was not a flag and could be resolved successfully, GDB
> steps directly towards the target and through the trampoline, hiding
> the trampoline from the user. If GDB cannot, however deduce a target,
> most likely because the DW_AT_trampoline was given as a flag or because
> of broken debug info, it will instead continue inside execution in the
> trampoline function until it reaches an instruction that is not
> associated with a trampoline function, which is usually the target
> function. It will then stop and give control back to the user.
> It should be noted, that there might be the cases, where trampolines
> call functions other than the target before the actual target call. If,
> in such a situation, GDB fails to resolve the target, it would resume
> execution until stepping into this other function call, and hand back
> control to the user, without actually having reached the target. A
> second step would have to be issued by the user to arrive a the target
> (by resuming in the trampoline and then until leaving it a second time).
> As this is a rather pathological case and no real instance of this is
> known, I think the current behavior here is good enough and seems to be
> the best GDB can do in such a situation.
>
> Secondly, as trampoline functions normally do not have any real source
> code correlation, it is likely that they mostly appear without line
> info.
> Normally, GDB would skip completely over a function call to a function
> that has no source line information, so we would never get to the
> aforementioned stepping through a trampoline and target resolution. To
> remedy this, for debug info trampolines, GDB now attempts to step through
> them regardless of them having source line information or not. So
> issuing a step at a function call wrapped by a trampoline without source
> line information will no longer skip the whole function call, but now
> step through the trampoline and attempt to resolve the trampoline target
> as described above (so usually, a single step at the call site will step
> through the trampoline and towards the target, even if the trampoline had
> not source line info).
>
> Last, in all other cases when GDB is about to stop at a location that is
> included in a trampoline region (e.g. after a step from the target back
> into the trampoline) GDB will instead continue until the trampoline
> region is left again and only then give control back to the user. This
> change serves the purpose of allowing stepping back from a target call
> through the trampoline without the user noticing the artificial function
> call inbetween call site and target.
>
> Together, these changes attempt to hide the trampoline function from the
> user while stepping. Additionally, the skip-trampoline-functions option
> has been introduced in infrun. It is set by default, and, when turned
> off, GDB will return to its 'normal' stepping behavior and ignore any
> possible DW_AT_trampoline.
I really like this feature. Unfortunately, this isn't working when first
entering a trampoline in reverse stepping. ie:
➜ gdb ./gdb -q
testsuite/outputs/gdb.dwarf2/dw2-function-trampolines/dw2-function-trampolines
Reading symbols from
testsuite/outputs/gdb.dwarf2/dw2-function-trampolines/dw2-function-trampolines...
(gdb) start
Temporary breakpoint 1, main ()
at
/home/blarsen/Documents/fsf_build/gdb/testsuite/../../../binutils-gdb/gdb/testsuite/gdb.dwarf2/dw2-function-trampolines.c:69
warning: Source file is more recent than executable.
69 global_var = 0; /* main set
global_var */
(gdb) record
(gdb) n
73 ans = trampoline (); /* main call
trampoline */
(gdb) n
75 ans = chained_trampoline (); /* main call
chained_trampoline */
(gdb) rs
0x000000000040114b in trampoline ()
(gdb)
Single stepping until exit from function trampoline,
which has no line number information.
target ()
at
/home/blarsen/Documents/fsf_build/gdb/testsuite/../../../binutils-gdb/gdb/testsuite/gdb.dwarf2/dw2-function-trampolines.c:28
28 return 9 + 10; /* target return */
(gdb)
26 ++global_var; /* target add */
(gdb)
main ()
at
/home/blarsen/Documents/fsf_build/gdb/testsuite/../../../binutils-gdb/gdb/testsuite/gdb.dwarf2/dw2-function-trampolines.c:73
73 ans = trampoline (); /* main call
trampoline */
(gdb)
I think this should be fixed before this patch went in.
--
Cheers,
Bruno
>
> As currently only ifx emits the DW_AT_trampoline tag, a test has been
> added to gdb.dwarf2 that artificially creates a set of trampoline
> functions.
>
> 2023-07-10 Nils-Christian Kempke <nils-christian.kempke@intel.com>
> ---
> gdb/NEWS | 15 ++
> gdb/doc/gdb.texinfo | 36 +++
> gdb/infrun.c | 81 +++++-
> gdb/infrun.h | 4 +
> .../gdb.dwarf2/dw2-function-trampolines.c | 80 ++++++
> .../gdb.dwarf2/dw2-function-trampolines.exp | 245 ++++++++++++++++++
> 6 files changed, 457 insertions(+), 4 deletions(-)
> create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-function-trampolines.c
> create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-function-trampolines.exp
>
> diff --git a/gdb/NEWS b/gdb/NEWS
> index b924834d3d7..8bca5540f58 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -571,6 +571,21 @@ platform.
>
> * New commands
>
> +set skip-trampoline-functions on|off
> +show skip-trampoline-functions
> + This setting is 'on' by default. When 'on' it controls whether GDB's
> + stepping behavior will recognize function calls that have been marked as
> + trampolines in the debug info. It improves stepping behavior in that it
> + steps through trampoline code and hides it from the user. GDB can now step
> + through trampolines that are correctly marked as such in the compiler's
> + debug info. If the target of a trampoline is unknown, GDB will continue
> + until the trampoline section is left again and only then hand control back
> + to the user. GDB does this even if the trampoline has no associated line
> + info. If this is turned off, GDB will step into trampolines if there is
> + line table information for them or step over the trampoline calls if there
> + is no line table information. Currently, only DWARF trampolines are
> + supported.
> +
> maint set backtrace-on-fatal-signal on|off
> maint show backtrace-on-fatal-signal
> This setting is 'on' by default. When 'on' GDB will print a limited
> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
> index b10c06ae91f..839c3e6a0f8 100644
> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -6391,6 +6391,42 @@ debug information. This is the default.
> Show whether @value{GDBN} will stop in or step over functions without
> source line debug information.
>
> +@kindex set skip-trampoline-functions
> +@cindex trampoline functions
> +@cindex stepping through trampoline functions
> +@item set skip-trampoline-functions
> +@itemx set skip-trampoline-functions on
> +When calling a function in any language, some compilers might generate
> +so-called @dfn{trampoline functions}, which wrap the actual function call
> +(the target of the trampoline). The compiler might mark such a trampoline in
> +its debug information. Often, such trampolines do not have any source line
> +information associated with them which will lead the @code{step} command to
> +behave like a @code{next} and skip the function call completely.
> +
> +The default is @code{set skip-trampoline-functions on} and it will cause the
> +@code{step} command to treat these trampolines differently. When issuing a
> +@code{step} at the call site of a trampoline function if
> +@code{skip-trampoline-functions} is set @value{GDBN} will attempt to determine
> +the target of the trampoline and then step through the trampoline stopping at
> +the target. If the target could not be found or was not given in the debug
> +info, @value{GDBN} will simply continue execution until it leaves the
> +trampoline code again, even if the trampoline has no line info associated with
> +it. When returning from a target function call and stepping back into the
> +trampoline, @value{GDBN} will again step through the trampoline towards the
> +call site. Additionally, even if stopped in a trampoline function with source
> +line information, issuing a @code{step} will prompt @value{GDBN} to resume
> +execution until leaving the trampoline region again. The @code{stepi} command
> +is not affected by the setting which is enabled by default. Currently, only
> +DWARF trampolines marked via DW_AT_trampoline are supported by this.
> +
> +@item set skip-trampoline-functions off
> +Causes the @code{step} command to completely ignore any trampoline information
> +a compiler might have emitted in its debug info. Trampolines will be treated
> +like any other function when stepping.
> +
> +@item show skip-trampoline-functions
> +Show whether @value{GDBN} tries to skip trampolines or not.
> +
> @kindex finish
> @kindex fin @r{(@code{finish})}
> @item finish
> diff --git a/gdb/infrun.c b/gdb/infrun.c
> index 58da1cef29e..dfafe7cdc74 100644
> --- a/gdb/infrun.c
> +++ b/gdb/infrun.c
> @@ -184,6 +184,12 @@ get_previous_thread ()
>
> static bool detach_fork = true;
>
> +/* If set (default) GDB will step through functions/inlined subroutines marked
> + DW_AT_trampoline by the compiler. If false, GDB will ignore the
> + attribute. */
> +
> +static bool skip_trampoline_functions = true;
> +
> bool debug_infrun = false;
> static void
> show_debug_infrun (struct ui_file *file, int from_tty,
> @@ -7265,7 +7271,6 @@ process_event_stop_test (struct execution_control_state *ecs)
> != find_pc_function (ecs->event_thread->stop_pc ())))))
> {
> CORE_ADDR stop_pc = ecs->event_thread->stop_pc ();
> - CORE_ADDR real_stop_pc;
>
> infrun_debug_printf ("stepped into subroutine");
>
> @@ -7337,8 +7342,41 @@ process_event_stop_test (struct execution_control_state *ecs)
> calling routine and the real function), locate the real
> function. That's what tells us (a) whether we want to step
> into it at all, and (b) what prologue we want to run to the
> - end of, if we do step into it. */
> - real_stop_pc = skip_language_trampoline (frame, stop_pc);
> + end of, if we do step into it. For functions marked as
> + trampoline functions we try to find their target and step
> + towards it (if skip_trampoline_functions is not set to false by the
> + user). If no target can be determined we just step into the
> + trampoline and hand control back to the user. */
> + CORE_ADDR real_stop_pc = 0;
> + bool in_trampoline = skip_trampoline_functions
> + && in_trampoline_function (stop_pc);
> +
> + if (in_trampoline)
> + {
> + real_stop_pc = find_function_trampoline_target (stop_pc);
> +
> + for (int i = 0; i < MAX_TRAMPOLINE_CHAIN_SIZE
> + && in_trampoline_function (real_stop_pc); ++i)
> + {
> + real_stop_pc = find_function_trampoline_target (real_stop_pc);
> + /* Exit if find_function_trampoline_target failed to find the
> + trampoline target. Do not try to resolve the trampolines
> + in this case. */
> + if (real_stop_pc == 0x0)
> + break;
> + }
> +
> + /* If we failed to find a target we will just single step in the
> + hope of leaving the trampoline again soon. */
> + if (real_stop_pc == 0x0)
> + {
> + keep_going (ecs);
> + return;
> + }
> + }
> +
> + if (real_stop_pc == 0)
> + real_stop_pc = skip_language_trampoline (frame, stop_pc);
> if (real_stop_pc == 0)
> real_stop_pc = gdbarch_skip_trampoline_code (gdbarch, frame, stop_pc);
> if (real_stop_pc != 0)
> @@ -7359,6 +7397,8 @@ process_event_stop_test (struct execution_control_state *ecs)
> /* If we have line number information for the function we are
> thinking of stepping into and the function isn't on the skip
> list, step into it.
> + If we are about to step into a function marked trampoline with no
> + line number information, we still want to enter it here.
>
> If there are several symtabs at that PC (e.g. with include
> files), just want to know whether *any* of them have line
> @@ -7367,7 +7407,9 @@ process_event_stop_test (struct execution_control_state *ecs)
> struct symtab_and_line tmp_sal;
>
> tmp_sal = find_pc_line (ecs->stop_func_start, 0);
> - if (tmp_sal.line != 0
> + if ((tmp_sal.line != 0
> + || (skip_trampoline_functions
> + && in_trampoline_function (ecs->stop_func_start)))
> && !function_name_is_marked_for_skip (ecs->stop_func_name,
> tmp_sal)
> && !inline_frame_is_marked_for_skip (true, ecs->event_thread))
> @@ -7520,6 +7562,19 @@ process_event_stop_test (struct execution_control_state *ecs)
> return;
> }
>
> + /* If we ended up in a function trampoline without stepping into a new
> + function we are either in some inlined trampoline or returning through a
> + trampoline function. In either case we continue to single step until we
> + are out of the trampoline code again. This check has to be done before
> + stop_pc_sal.line == 0 below, as trampolines usually don't have source
> + line information associated with them. */
> + if (skip_trampoline_functions && in_trampoline_function (stop_pc_sal.pc))
> + {
> + infrun_debug_printf ("stepped into trampoline code");
> + keep_going (ecs);
> + return;
> + }
> +
> if (stop_pc_sal.line == 0)
> {
> /* We have no line number information. That means to stop
> @@ -9731,6 +9786,14 @@ show_exec_direction_func (struct ui_file *out, int from_tty,
> }
> }
>
> +static void
> +show_skip_trampoline_functions (ui_file *file, int from_tty,
> + cmd_list_element *c,
> + const char *value)
> +{
> + gdb_printf (file, _("Skipping trampoline functions is %s.\n"), value);
> +}
> +
> static void
> show_schedule_multiple (struct ui_file *file, int from_tty,
> struct cmd_list_element *c, const char *value)
> @@ -10076,6 +10139,16 @@ Options are 'forward' or 'reverse'."),
> set_exec_direction_func, show_exec_direction_func,
> &setlist, &showlist);
>
> + add_setshow_boolean_cmd ("skip-trampoline-functions", class_run,
> + &skip_trampoline_functions, _("\
> +Set whether gdb attempts to hide trampolines marked in the debug info."), _("\
> +Show whether gdb attempts to hide trampolines marked in the debug info."), _("\
> +If on, while stepping gdb will skip through functions and inlined functions\n\
> +marked as trampolines by the compiler. If off, gdb will ignore such function\n\
> +trampolines."),
> + nullptr, show_skip_trampoline_functions, &setlist,
> + &showlist);
> +
> /* Set/show detach-on-fork: user-settable mode. */
>
> add_setshow_boolean_cmd ("detach-on-fork", class_run, &detach_fork, _("\
> diff --git a/gdb/infrun.h b/gdb/infrun.h
> index a343d27f72d..d4a6b7892b5 100644
> --- a/gdb/infrun.h
> +++ b/gdb/infrun.h
> @@ -76,6 +76,10 @@ infrun_debug_show_threads (const char *title, ThreadRange threads)
> }
>
>
> +/* Maximum size of trampoline chain to process while resolving
> + trampolines. */
> +#define MAX_TRAMPOLINE_CHAIN_SIZE 10
> +
> /* Nonzero if we want to give control to the user when we're notified
> of shared library events by the dynamic linker. */
> extern int stop_on_solib_events;
> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-function-trampolines.c b/gdb/testsuite/gdb.dwarf2/dw2-function-trampolines.c
> new file mode 100644
> index 00000000000..1f0eb2e0efa
> --- /dev/null
> +++ b/gdb/testsuite/gdb.dwarf2/dw2-function-trampolines.c
> @@ -0,0 +1,80 @@
> +/* Copyright 2023 Free Software Foundation, Inc.
> +
> + 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/>. */
> +
> +/* This test relies on inlined_trampoline being inlined into main and the other
> + functions not. All functions except target will be marked via
> + DW_AT_trampoline in the debug info and we'll check whether one can step
> + through the trampolines towards target. */
> +volatile int global_var;
> +
> +int __attribute__ ((noinline))
> +target () /* target decl line */
> +{ /* target prologue */
> + asm ("target_label: .globl target_label");
> + ++global_var; /* target add */
> + asm ("target_label2: .globl target_label2");
> + return 9 + 10; /* target return */
> +} /* target end */
> +
> +int __attribute__ ((noinline))
> +trampoline ()
> +{ /* trampoline prologue */
> + asm ("trampoline_label: .globl trampoline_label");
> + ++global_var;
> + return target (); /* trampoline target call */
> +} /* trampoline end */
> +
> +static inline int __attribute__ ((always_inline))
> +inlined_trampoline ()
> +{ /* inlined_trampoline prologue */
> + asm ("inlined_trampoline_label: .globl inlined_trampoline_label");
> + ++global_var; /* inlined_trampoline add */
> + asm ("inlined_trampoline_label2: .globl inlined_trampoline_label2");
> + return target (); /* inlined_trampoline target call */
> +} /* inlined_trampoline end */
> +
> +int __attribute__ ((noinline))
> +chained_trampoline ()
> +{ /* chained_trampoline prologue */
> + asm ("chained_trampoline_label: .globl chained_trampoline_label");
> + ++global_var;
> + return trampoline (); /* chained_trampoline trampoline call */
> +} /* chained_trampoline end */
> +
> +int __attribute__ ((noinline))
> +doubly_chained_trampoline ()
> +{ /* doubly_chained_trampoline prologue */
> + asm ("doubly_chained_trampoline_label: .globl doubly_chained_trampoline_label");
> + ++global_var;
> + return chained_trampoline (); /* doubly_chained_trampoline chained_trampoline call */
> +} /* doubly_chained_trampoline end */
> +
> +int
> +main () /* main decl line */
> +{ /* main prologue */
> + int ans;
> + asm ("main_label: .globl main_label");
> + global_var = 0; /* main set global_var */
> + asm ("main_label2: .globl main_label2");
> + ans = inlined_trampoline (); /* main call inlined_trampoline */
> + asm ("main_label3: .globl main_label3");
> + ans = trampoline (); /* main call trampoline */
> + asm ("main_label4: .globl main_label4");
> + ans = chained_trampoline (); /* main call chained_trampoline */
> + asm ("main_label5: .globl main_label5");
> + ans = doubly_chained_trampoline (); /* main call doubly_chained_trampoline */
> + asm ("main_label6: .globl main_label6");
> + return ans; /* main call return */
> +} /* main end */
> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-function-trampolines.exp b/gdb/testsuite/gdb.dwarf2/dw2-function-trampolines.exp
> new file mode 100644
> index 00000000000..67eec2b5540
> --- /dev/null
> +++ b/gdb/testsuite/gdb.dwarf2/dw2-function-trampolines.exp
> @@ -0,0 +1,245 @@
> +# Copyright 2023 Free Software Foundation, Inc.
> +
> +# 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/>.
> +
> +# This test checks GDB's handling of functions/inlined functions marked
> +# DW_AT_trampoline by the compiler. A function marked as trampoline should
> +# generally be hidden from the user. We check whether we can step through
> +# trampolines. Every trampoline is defined using a different type for its
> +# target: a string, an address, a DIE reference or a flag.
> +# Setting skip-trampoline-functions to false inside GDB should make it return
> +# to its 'normal' behavior, ignore the DW_AT_trampoline, and skip all of the
> +# non-inlined trampoline calls (as their DIEs don't have any source
> +# information).
> +
> +load_lib dwarf.exp
> +
> +# This test can only be run on targets which support DWARF-2 and use gas.
> +if {![dwarf2_support]} {
> + return 0
> +}
> +
> +standard_testfile .c .S
> +
> +set asm_file [standard_output_file $srcfile2]
> +Dwarf::assemble $asm_file {
> + global srcdir subdir srcfile
> + declare_labels lines_label trampoline_label
> +
> + get_func_info target
> + get_func_info trampoline
> + get_func_info chained_trampoline
> + get_func_info doubly_chained_trampoline
> + get_func_info main
> +
> + set target_decl_line [gdb_get_line_number "target decl line"]
> + set main_decl_line [gdb_get_line_number "main decl line"]
> + set main_call_inlined_trampoline_line [gdb_get_line_number "main call inlined_trampoline"]
> +
> + cu {} {
> + compile_unit {
> + {language @DW_LANG_C}
> + {name dw2-function-trampolines.c}
> + {low_pc 0 addr}
> + {stmt_list ${lines_label} DW_FORM_sec_offset}
> + } {
> + subprogram {
> + {name target}
> + {low_pc $target_start addr}
> + {high_pc "$target_start + $target_len" addr}
> + {decl_file 1 data1}
> + {decl_line $target_decl_line data1}
> + }
> + # The 'trampoline' subprogram declares its target by name.
> + trampoline_label: subprogram {
> + {name trampoline}
> + {low_pc $trampoline_start addr}
> + {high_pc "$trampoline_start + $trampoline_len" addr}
> + {trampoline target string}
> + }
> + # The 'chained_trampoline' subprogram declares its target as die
> + # reference.
> + subprogram {
> + {name chained_trampoline}
> + {low_pc $chained_trampoline_start addr}
> + {high_pc "$chained_trampoline_start + $chained_trampoline_len" addr}
> + {trampoline %$trampoline_label}
> + }
> + # The 'doubly_chained_trampoline' subprogram declares no target.
> + # Its DW_AT_trampoline is a flag set to true.
> + subprogram {
> + {name doubly_chained_trampoline}
> + {low_pc $doubly_chained_trampoline_start addr}
> + {high_pc "$doubly_chained_trampoline_start + $doubly_chained_trampoline_len" addr}
> + {trampoline 1 flag}
> + }
> + subprogram {
> + {external 1 flag}
> + {name main}
> + {main_subprogram 1 flag}
> + {low_pc $main_start addr}
> + {high_pc "$main_start + $main_len" addr}
> + {decl_file 1 data1}
> + {decl_line $main_decl_line data1}
> + } {
> + # The 'inlined_trampoline' subroutine declares its target as
> + # an address.
> + inlined_subroutine {
> + {name inlined_trampoline}
> + {low_pc main_label2 addr}
> + {high_pc main_label3 addr}
> + {trampoline $target_start addr}
> + {call_file 1 data1}
> + {call_line $main_call_inlined_trampoline_line data1}
> + }
> + }
> + }
> + }
> +
> + lines {version 2} lines_label {
> + include_dir "${srcdir}/${subdir}"
> + file_name "$srcfile" 1
> +
> + program {
> + DW_LNE_set_address $main_start
> + line [gdb_get_line_number "main set global_var"]
> + DW_LNS_copy
> + DW_LNE_set_address main_label
> + line [gdb_get_line_number "main set global_var"]
> + DW_LNS_copy
> + DW_LNE_set_address main_label2
> + line [gdb_get_line_number "main call inlined_trampoline"]
> + DW_LNS_copy
> + DW_LNE_set_address inlined_trampoline_label
> + line [gdb_get_line_number "inlined_trampoline add"]
> + DW_LNS_copy
> + DW_LNE_set_address inlined_trampoline_label2
> + line [gdb_get_line_number "inlined_trampoline target call"]
> + DW_LNS_copy
> + DW_LNE_set_address main_label3
> + line [gdb_get_line_number "main call trampoline"]
> + DW_LNS_copy
> + DW_LNE_set_address main_label4
> + line [gdb_get_line_number "main call chained_trampoline"]
> + DW_LNS_copy
> + DW_LNE_set_address main_label5
> + line [gdb_get_line_number "main call doubly_chained_trampoline"]
> + DW_LNS_copy
> + DW_LNE_set_address main_label6
> + line [gdb_get_line_number "main call return"]
> + DW_LNS_copy
> + DW_LNE_set_address $main_end
> + DW_LNE_end_sequence
> +
> + DW_LNE_set_address $target_start
> + line [gdb_get_line_number "target prologue"]
> + DW_LNS_negate_stmt
> + DW_LNS_copy
> + DW_LNE_set_address target_label
> + line [gdb_get_line_number "target add"]
> + DW_LNS_negate_stmt
> + DW_LNS_copy
> + DW_LNE_set_address target_label2
> + line [gdb_get_line_number "target return"]
> + DW_LNS_copy
> + DW_LNE_set_address $target_end
> + DW_LNE_end_sequence
> + }
> + }
> +}
> +
> +if {[prepare_for_testing "failed to prepare" ${testfile} \
> + [list $srcfile $asm_file] {nodebug additional_flags=-O0}]} {
> + return -1
> +}
> +
> +set target_first_line_pattern ".*target add.*"
> +set target_second_line_pattern ".*target return.*"
> +
> +if ![runto_main] {
> + return -1
> +}
> +
> +gdb_test "show skip-trampoline-functions" \
> + "Skipping trampoline functions is on\." \
> + "check skip-trampoline-functions is enabled"
> +
> +with_test_prefix "with trampoline handling" {
> + foreach {trampoline return_line} { "inlined_trampoline" "trampoline" \
> + "trampoline" "chained_trampoline" \
> + "chained_trampoline" "doubly_chained_trampoline" } {
> +
> + gdb_test "s" "$target_first_line_pattern" "step through $trampoline"
> + gdb_test "s" "$target_second_line_pattern" \
> + "step target second line from $trampoline"
> + gdb_test "s" ".*main call $return_line.*" \
> + "step back through $trampoline"
> + }
> +
> + # The doubly_chained_trampoline has only been marked as trampoline but no
> + # target was given. In this case GDB steps into the trampoline and then
> + # continues until the trampoline section is left again.
> +
> + # When compiled with gcc 7.5 (and possibly others) on a 32 bit system, the
> + # trampoline function contains a call to __x86.get_pc_thunk.ax before the
> + # actual target call. So, we end up in __x86.get_pc_thunk.ax. Issuing a
> + # second step command will return from the function call back into the
> + # trampoline and go on inside the trampoline towards the actual target call.
> + # On other targets we step directly towards the target call.
> + gdb_test_multiple "s" "step through double_chained_trampoline" {
> + -re -wrap "$target_first_line_pattern" {
> + pass $gdb_test_name
> + }
> + -re -wrap ".*__x86.get_pc_thunk.ax.*" {
> + gdb_test "s" "$target_first_line_pattern" \
> + "step through double_chained_trampoline 2nd try"
> + }
> + }
> + gdb_test "s" "$target_second_line_pattern" \
> + "step target second line fromdoubly_chained_trampoline"
> + gdb_test "s" ".*main call return.*" \
> + "step back through doubly_chained_trampoline"
> +}
> +
> +clean_restart ${testfile}
> +
> +if ![runto_main] {
> + return -1
> +}
> +
> +gdb_test_no_output "set skip-trampoline-functions off" \
> + "disable trampoline handling"
> +gdb_test "show skip-trampoline-functions" \
> + "Skipping trampoline functions is off." \
> + "check skip-trampoline-functions is disabled"
> +
> +with_test_prefix "without trampoline handling" {
> + gdb_test "s" ".*main call inlined_trampoline.*"
> + gdb_test "s" ".*inlined_trampoline add.*" \
> + "step into inlined_trampoline with skip-trampoline off"
> + gdb_test "s" ".*inlined_trampoline target call.*" \
> + "step in inlined_trampoline with skip-trampoline off"
> + gdb_test "s" "$target_first_line_pattern" \
> + "step into target with skip-trampoline off"
> + gdb_test "s" "$target_second_line_pattern" \
> + "step second line in target with skip-trampoline off"
> + gdb_test "s" ".*main call trampoline.*" \
> + "step brack from target with skip-trampoline off"
> + gdb_test "s" ".*main call chained_trampoline.*" \
> + "skip trampoline call with no line info"
> + gdb_test "s" ".*main call doubly_chained_trampoline.*" \
> + "skip chained_trampoline call with no line info"
> + gdb_test "s" ".*main call return.*" \
> + "skip doubly_chained_trampoline call with no line info"
> +}
More information about the Gdb-patches
mailing list