From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTPS id 39D743858C2F for ; Thu, 27 Jul 2023 11:46:48 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 39D743858C2F Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1690458407; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=8SZdY2JopluIF8sj4RZA5yQfZgCW6vW1tIw6W+8FbWE=; b=Xq/s0eMlws2wc21ykH/OvUZyTevNsfl63DXrZ341w7udvgtw7SchHaDiHihgXZKcPmVLJG g8Ufr9iFX+n/Fh0Kp4jdgomGSVuFPjw9lNXvICNXuq+U+NMaeJJ1X5Ct7mbX2M3jhIpkH7 YBpvle8W8Ce3UM7VUQRI1upUDb8MsVs= Received: from mail-wr1-f70.google.com (mail-wr1-f70.google.com [209.85.221.70]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-692-lolxMdIFPyi8LGraPd_N4Q-1; Thu, 27 Jul 2023 07:46:46 -0400 X-MC-Unique: lolxMdIFPyi8LGraPd_N4Q-1 Received: by mail-wr1-f70.google.com with SMTP id ffacd0b85a97d-31775a8546fso476094f8f.3 for ; Thu, 27 Jul 2023 04:46:46 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1690458405; x=1691063205; h=content-transfer-encoding:in-reply-to:from:references:cc:to :content-language:subject:user-agent:mime-version:date:message-id :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=8SZdY2JopluIF8sj4RZA5yQfZgCW6vW1tIw6W+8FbWE=; b=cm7PVbFoLZCbb1qUSkEuAAA/KJBDVfVhm0oWiWSHEklKCwAB8D9NuoIQhToJ5kXncy M5WZMR2dfrw9hrUDYSvwvcoF3Mc0OT8cCFQbJB+olMGQQSHP+UgkCx3f4EGueaoSVhlM 5gqX4/sO/HKnaE4EaaiLfI4iP/O44GZG5Gph9sEylPEM/G6YoCR/3VLuenKljDVnM2Oq Ob37ZHJoJ49qUm5gkiCwkFYuRCxgmFbptEKZMqC1jUSqH0Q/ICLGXvVPRZ6Elv8RQv/o bySU3BIX0DE+rGJichBO58NZfvintelXbqukw06/+Nw3YUfw+/CaWCjqobPT5WSc2eJP 6Cng== X-Gm-Message-State: ABy/qLZceqPEHDFQx7dF+dQ6Siz7y8YC/GSuAAE2S6UDJJYAjmfk6xEP JIjK/AuVuwPMQq1yf+1d7gfjqOy+hrHWc/+GuvC6fb4TQK+2yVo+XpdStj08BREG1U+NCnyurkb DGi+e85YmIFybIrxzyhyFhQ== X-Received: by 2002:a05:6000:1111:b0:313:f61c:42ab with SMTP id z17-20020a056000111100b00313f61c42abmr1699869wrw.56.1690458405027; Thu, 27 Jul 2023 04:46:45 -0700 (PDT) X-Google-Smtp-Source: APBJJlFJQ0VgSTp7RlnFan5EcLxfwIzOHpgMKUe8QKmC0X9mfqwb8NQtjhlYZZRBG158zTqaNMYwAg== X-Received: by 2002:a05:6000:1111:b0:313:f61c:42ab with SMTP id z17-20020a056000111100b00313f61c42abmr1699847wrw.56.1690458404437; Thu, 27 Jul 2023 04:46:44 -0700 (PDT) Received: from [192.168.0.129] (ip-94-112-225-44.bb.vodafone.cz. [94.112.225.44]) by smtp.gmail.com with ESMTPSA id s10-20020adfecca000000b003177f57e79esm1796696wro.88.2023.07.27.04.46.43 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Thu, 27 Jul 2023 04:46:44 -0700 (PDT) Message-ID: <3a9e7a38-3326-f7f2-b90b-b7286560debf@redhat.com> Date: Thu, 27 Jul 2023 13:46:42 +0200 MIME-Version: 1.0 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Thunderbird/102.12.0 Subject: Re: [PATCH v3 3/4] gdb/infrun: handle stepping through functions with DW_AT_trampoline To: Abdul Basit Ijaz , gdb-patches@sourceware.org Cc: JiniSusan.George@amd.com, tom@tromey.com, eliz@gnu.org, Nils-Christian Kempke References: <20230710225643.32280-1-abdul.b.ijaz@intel.com> <20230710225643.32280-4-abdul.b.ijaz@intel.com> From: Bruno Larsen In-Reply-To: <20230710225643.32280-4-abdul.b.ijaz@intel.com> X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Language: en-US Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-8.4 required=5.0 tests=BAYES_00,BODY_8BITS,DKIMWL_WL_HIGH,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,GIT_PATCH_0,KAM_SHORT,NICE_REPLY_A,RCVD_IN_BARRACUDACENTRAL,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H4,RCVD_IN_MSPIKE_WL,SPF_HELO_NONE,SPF_NONE,TXREP,T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org List-Id: On 11/07/2023 00:56, Abdul Basit Ijaz via Gdb-patches wrote: > From: Nils-Christian Kempke > > 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 > --- > 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 . */ > + > +/* 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 . > + > +# 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" > +}