# Copyright 2026 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 . # Test that GDB can unwind using a .debug_frame section generated by # the DWARF assembler. # # This test is amd64-specific, but could be ported to other # architectures if needed. load_lib dwarf.exp require dwarf2_support is_x86_64_m64_target standard_testfile .S -dw.S # AMD64 DWARF register numbers. set rax 0 set rbp 6 set rsp 7 set r12 12 set r13 13 set r14 14 set rip 16 foreach_with_prefix is_64 { false true } { set asm_file [standard_output_file ${testfile}-${is_64}-dw.S] Dwarf::assemble $asm_file { frame { declare_labels cie_label cie_label: CIE { return_address_register $::rip data_alignment_factor -8 is_64 $::is_64 } { DW_CFA_def_cfa $::rsp 8 DW_CFA_offset $::rip 1 } # FDE for main FDE $cie_label main main_len { is_64 $::is_64 } { DW_CFA_set_loc main_after_push_rbp DW_CFA_def_cfa_offset 16 DW_CFA_offset $::rbp 2 DW_CFA_set_loc main_after_set_rbp DW_CFA_def_cfa_register $::rbp } # FDE for caller FDE $cie_label caller caller_len { is_64 $::is_64 } { DW_CFA_set_loc caller_after_push_rbp DW_CFA_def_cfa_offset 16 DW_CFA_offset $::rbp 2 DW_CFA_set_loc caller_after_set_rbp DW_CFA_def_cfa_register $::rbp } # FDE for callee FDE $cie_label callee callee_len { is_64 $::is_64 } { DW_CFA_set_loc callee_after_push_rbp DW_CFA_def_cfa_offset 16 DW_CFA_offset $::rbp 2 DW_CFA_set_loc callee_after_set_rbp DW_CFA_def_cfa_register $::rbp DW_CFA_set_loc callee_body DW_CFA_offset $::r12 3 DW_CFA_register $::r13 $::rax # r14's value is computed by an arbitrary expression. DW_CFA_val_expression $::r14 { DW_OP_constu 0x99aabbcc } } } } if { [prepare_for_testing "failed to prepare" ${testfile}-${is_64} \ [list $srcfile $asm_file] {nodebug}] } { continue } # Stop in caller before the call, to capture rbp. if { ![runto caller_call_callee] } { continue } set caller_rbp [get_hexadecimal_valueof "\$rbp" "UNKNOWN"] # Stop inside callee. gdb_breakpoint callee_body gdb_continue_to_breakpoint "callee_body" # Verify backtrace shows the full call chain. gdb_test "bt" "#0.*callee.*\r\n#1.*caller.*\r\n#2.*main.*" # Select caller's frame and check saved registers. gdb_test "frame 1" "#1.*caller.*" # r12 was saved on the stack by callee. gdb_test "p/x \$r12" "= 0x11223344" # r13 was saved in rax by callee. gdb_test "p/x \$r13" "= 0x55667788" # r14's value is computed by a DWARF expression. gdb_test "p/x \$r14" "= 0x99aabbcc" # rbp should match what caller had. gdb_test "p/x \$rbp" "= ${caller_rbp}" }