--- /dev/null
+/* Copyright 2020 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/>. */
+
+#include <asm/unistd.h>
+
+/* Define these for each architecture:
+
+ 1) RETURN_ADDRESS_REGNO: The register number representing the return
+ address in the DWARF CFI. It can be easily be looked up using
+ `readelf --debug-dump=frames-interp` on an existing binary of that
+ architecture, where it says `ra=X`.
+
+ 2) exit_0: a sequence of instruction to execute the exit syscall with
+ argument 0. */
+
+#if defined(__x86_64__)
+
+# define RETURN_ADDRESS_REGNO 16
+
+.macro exit_0
+ mov $__NR_exit, %rax
+ mov $0, %rdi
+ syscall
+.endm
+
+#elif defined(__i386__)
+
+# define RETURN_ADDRESS_REGNO 8
+
+.macro exit_0
+ mov $__NR_exit, %eax
+ mov $0, %ebx
+ int $0x80
+.endm
+
+#elif defined(__aarch64__)
+
+# define RETURN_ADDRESS_REGNO 30
+
+.macro exit_0
+ mov x0, #0
+ mov x8, #__NR_exit
+ svc #0
+.endm
+
+#elif defined(__arm__)
+
+# define RETURN_ADDRESS_REGNO 14
+
+.macro exit_0
+ ldr r7, =__NR_exit
+ ldr r0, =0
+ swi 0x0
+.endm
+
+#else
+# error "Unsupported architecture"
+#endif
+
+/* The following assembly program mimics this pseudo C program, where
+ everything has been inlined:
+
+ 1 void bar(void) {
+ 2 nop;
+ 3 }
+ 4
+ 5 void foo(void) {
+ 6 nop;
+ 7 bar();
+ 8 nop;
+ 9 }
+ 10
+ 11 void _start(void) {
+ 12 nop;
+ 13 foo();
+ 14 nop;
+ 15 exit(0);
+ 16 }
+*/
+
+.global _start
+_start:
+.cfi_startproc
+
+/* State that the return address for this frame is undefined. */
+.cfi_undefined RETURN_ADDRESS_REGNO
+
+.global __cu_low_pc
+__cu_low_pc:
+
+.global __start_low_pc
+__start_low_pc:
+ /* Line 12 */
+ nop
+
+.global __foo_low_pc
+__foo_low_pc:
+ /* Line 6 */
+ nop
+
+.global __bar_low_pc
+__bar_low_pc:
+ /* Line 2 */
+ nop
+
+.global __bar_high_pc
+__bar_high_pc:
+ /* Line 8 */
+ nop
+
+.global __foo_high_pc
+__foo_high_pc:
+ /* Line 14 */
+ nop
+
+ /* Line 15 */
+ exit_0
+
+.cfi_endproc
+
+.global __start_high_pc
+__start_high_pc:
+
+.global __cu_high_pc
+__cu_high_pc:
--- /dev/null
+# Copyright 2020 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/>.
+
+# Test unwinding when we have a frame inlined in the outer frame (in the sense
+# of frame.c:outer_frame_id).
+#
+# The conditions required to reproduce the original issue are:
+#
+# 1. Have an outer frame whose DWARF CFI explicitly says that the frame return
+# address is undefined.
+# 2. A frame inlined in this other frame.
+#
+# Because of (1), the test has to be written in assembly with explicit CFI
+# directives.
+
+load_lib dwarf.exp
+
+if {![dwarf2_support]} {
+ return 0
+}
+
+standard_testfile .S
+
+set dwarf_asm [standard_output_file dwarf-asm.S]
+Dwarf::assemble $dwarf_asm {
+ declare_labels foo_subprogram bar_subprogram
+ declare_labels stmt_list
+
+ # See the comment in the .S file for the equivalent C program this is meant
+ # to represent.
+
+ cu { addr_size 4 } {
+ DW_TAG_compile_unit {
+ {DW_AT_name file1.txt}
+ {DW_AT_stmt_list $stmt_list DW_FORM_sec_offset}
+ {DW_AT_language @DW_LANG_C99}
+ {DW_AT_low_pc __cu_low_pc DW_FORM_addr}
+ {DW_AT_high_pc __cu_high_pc DW_FORM_addr}
+ } {
+ DW_TAG_subprogram {
+ {DW_AT_name "_start"}
+ {DW_AT_low_pc __start_low_pc DW_FORM_addr}
+ {DW_AT_high_pc __start_high_pc DW_FORM_addr}
+ } {
+ DW_TAG_inlined_subroutine {
+ {DW_AT_abstract_origin :$foo_subprogram}
+ {DW_AT_low_pc __foo_low_pc DW_FORM_addr}
+ {DW_AT_high_pc __foo_high_pc DW_FORM_addr}
+ {DW_AT_call_file 1 DW_FORM_data1}
+ {DW_AT_call_line 13 DW_FORM_data1}
+ } {
+ DW_TAG_inlined_subroutine {
+ {DW_AT_abstract_origin :$bar_subprogram}
+ {DW_AT_low_pc __bar_low_pc DW_FORM_addr}
+ {DW_AT_high_pc __bar_high_pc DW_FORM_addr}
+ {DW_AT_call_file 1 DW_FORM_data1}
+ {DW_AT_call_line 7 DW_FORM_data1}
+ }
+ }
+ }
+
+ foo_subprogram: DW_TAG_subprogram {
+ {DW_AT_name "foo"}
+ {DW_AT_prototyped 1 DW_FORM_flag_present}
+ {DW_AT_inline 0x1 DW_FORM_data1}
+ }
+
+ bar_subprogram: DW_TAG_subprogram {
+ {DW_AT_name "bar"}
+ {DW_AT_prototyped 1 DW_FORM_flag_present}
+ {DW_AT_inline 0x1 DW_FORM_data1}
+ }
+ }
+ }
+
+ lines { } stmt_list {
+ global srcdir subdir srcfile
+
+ include_dir "/some/directory"
+ file_name "/some/directory/file.c" 0
+ }
+}
+
+if { [build_executable ${testfile}.exp ${testfile} "$srcfile $dwarf_asm" \
+ {additional_flags=-nostdlib additional_flags=-static}] != 0 } {
+ untested "failed to compile"
+ return
+}
+
+clean_restart $binfile
+
+if { [gdb_starti_cmd] != 0 } {
+ fail "failed to run to first instruction"
+ return
+}
+
+gdb_test "frame" "in _start .*"
+
+gdb_test "stepi" "in foo .*" "step into foo"
+gdb_test "stepi" "in bar .*" "step into bar"
+gdb_test "stepi" "in foo .*" "step back into foo"
+gdb_test "stepi" "in _start .*" "step back into _start"