blob: ddadab7566efca4b8d34ae128944c73ecd185a07 [file]
# 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 <http://www.gnu.org/licenses/>.
# 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}"
}