# Copyright 2010-2018 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 file is part of the gdb testsuite. # Test arm displaced stepping. if {![is_aarch32_target]} then { verbose "Skipping arm displaced stepping tests." return } standard_testfile .S if { [prepare_for_testing "failed to prepare" $testfile $srcfile] } { return -1 } ######################################### # Test ldm/stm related to PC. proc test_ldm_stm_pc {} { global srcfile global gdb_prompt # Try to set breakpoint on test_ldm_stm_pc. If symbol 'test_ldm_stm_pc' # can't be resolved, test case is compiled in Thumb mode, skip it. gdb_test_multiple "break *test_ldm_stm_pc" "break test_ldm_stm_pc" { -re "Breakpoint.*at.* file .*$srcfile, line.*\r\n$gdb_prompt $" { pass "break test_ldm_stm_pc" } -re "No symbol.*\r\n$gdb_prompt $" { pass "break test_ldm_stm_pc" return 0 } } gdb_test "break *test_ldm_pc" \ "Breakpoint.*at.* file .*$srcfile, line.*" \ "break test_ldm_pc" gdb_test "break *test_ldm_stm_pc_ret" \ "Breakpoint.*at.* file .*$srcfile, line.*" \ "break test_ldm_stm_pc_ret" gdb_continue_to_breakpoint "continue to test_ldm_stm_pc" \ ".*stmdb.*sp\!\,.*\{lr\, pc\}.*" gdb_continue_to_breakpoint "continue to test_ldm_pc" \ ".*ldmia.*sp\!\,.*\{pc\}.*" gdb_continue_to_breakpoint "continue to test_ldm_stm_pc_ret" \ ".*bx lr.*" } ######################################### # Test ldrX literal proc test_ldr_literal {} { global srcfile global gdb_prompt gdb_test_multiple "break *test_ldr_literal" "break test_ldr_literal" { -re "Breakpoint.*at.* file .*$srcfile, line.*\r\n$gdb_prompt $" { pass "break test_ldr_literal" } -re "No symbol.*\r\n$gdb_prompt $" { return 0 } } gdb_test "break *test_ldrsb_literal" \ "Breakpoint.*at.* file .*$srcfile, line.*" \ "break test_ldrsb_literal" gdb_test "break *test_ldrsh_literal" \ "Breakpoint.*at.* file .*$srcfile, line.*" \ "break test_ldrsh_literal" gdb_test "break *test_ldr_literal_end" \ "Breakpoint.*at.* file .*$srcfile, line.*" \ "break test_test_ldr_literal_end" gdb_continue_to_breakpoint "continue to test_ldr_literal" \ ".*ldrh.*r0\,.*\[pc\].*" gdb_continue_to_breakpoint "continue to test_ldrsb_literal" \ ".*ldrsb.*r0\,.*\[pc\].*" gdb_continue_to_breakpoint "continue to test_ldrsh_literal" \ ".*ldrsh.*r0\,.*\[pc\].*" gdb_continue_to_breakpoint "continue to test_ldr_literal_ret" \ ".*bx lr.*" } proc test_ldr_literal_16 {} { global srcfile global gdb_prompt gdb_test_multiple "break *test_ldr_literal_16" "break test_ldr_literal_16" { -re "Breakpoint.*at.* file .*$srcfile, line.*\r\n$gdb_prompt $" { pass "break test_ldr_literal" } -re "No symbol.*\r\n$gdb_prompt $" { return 0 } } gdb_test "break *test_ldr_literal_16_end" \ "Breakpoint.*at.* file .*$srcfile, line.*" \ "break test_ldr_literal_16_end" gdb_continue_to_breakpoint "continue to test_ldr_literal_16" \ ".*ldr.*r0\,.*L2.*" gdb_continue_to_breakpoint "continue to test_ldr_literal_16_end" \ ".*bx lr.*" } ########################################## # Test call/ret. proc test_call_ret {} { global srcfile global testfile gdb_test "break *test_call" \ "Breakpoint.*at.* file .*$srcfile, line.*" \ "break test_call" gdb_test "break *test_call_end" \ "Breakpoint.*at.* file .*$srcfile, line.*" \ "break test_call_end" gdb_test "break *test_ret" \ "Breakpoint.*at.* file .*$srcfile, line.*" \ "break test_ret" gdb_test "break *test_ret_end" \ "Breakpoint.*at.* file .*$srcfile, line.*" \ "break test_ret_end" gdb_continue_to_breakpoint "test_call" ".*bl test_call_subr.*" gdb_continue_to_breakpoint "test_call_end" \ ".*@ Location test_call_end.*" gdb_continue_to_breakpoint "test_ret" \ ".*bx lr.*" gdb_continue_to_breakpoint "continue to test_ret_end" \ ".*@ Location test_ret_end.*" } ######################################### # Test branch proc test_branch {} { global srcfile gdb_test "break *test_branch" \ "Breakpoint.*at.* file .*$srcfile, line.*" \ "break test_branch" gdb_test "break *L_branch" \ "Breakpoint.*at.* file .*$srcfile, line.*" \ "break Lbranch" gdb_continue_to_breakpoint "continue to test_branch" \ ".*b.*L_branch.*" gdb_continue_to_breakpoint "continue to Lbranch" \ ".*bx lr.*" } ######################################### # Test ldr from pc proc test_ldr_from_pc {} { global srcfile gdb_test "break *test_ldr_pc" \ "Breakpoint.*at.* file .*$srcfile, line.*" \ "break test_ldr_pc" gdb_test "break test_ldr_pc_ret" \ "Breakpoint.*at.* file .*$srcfile, line.*" \ "break test_ldr_pc_ret" gdb_continue_to_breakpoint "continue to test_ldr_pc" \ ".*ldr.*r1\,.*\[pc, #0\].*" gdb_continue_to_breakpoint "continue to test_ldr_pc_ret" \ ".*bx lr.*" } ######################################### # Test cbz and cbnz proc test_cbz_cbnz {} { global srcfile global gdb_prompt gdb_test_multiple "break *test_zero_cbnz" "break test_zero_cbnz" { -re "Breakpoint.*at.* file .*$srcfile, line.*\r\n$gdb_prompt $" { pass "break test_ldr_literal" } -re "No symbol.*\r\n$gdb_prompt $" { return 0 } } gdb_test "break *test_zero_cbz" \ "Breakpoint.*at.* file .*$srcfile, line.*" \ "break test_zero_cbz" gdb_test "break *test_non_zero_cbnz" \ "Breakpoint.*at.* file .*$srcfile, line.*" \ "break test_non_zero_cbnz" gdb_test "break *test_non_zero_cbz" \ "Breakpoint.*at.* file .*$srcfile, line.*" \ "break test_non_zero_cbz" gdb_continue_to_breakpoint "continue to test_zero_cbnz" \ ".*cbnz.*r0\,.*\.L3.*" gdb_continue_to_breakpoint "continue to test_zero_cbz" \ ".*cbz.*r0\,.*\.L3.*" gdb_continue_to_breakpoint "continue to test_non_zero_cbz" \ ".*cbz.*r0\,.*\.L4.*" gdb_continue_to_breakpoint "continue to test_non_zero_cbnz" \ ".*cbnz.*r0\,.*\.L4.*" } # Test adr proc test_adr {} { global srcfile global gdb_prompt gdb_test_multiple "break *test_adr" "break test_adr" { -re "Breakpoint.*at.* file .*$srcfile, line.*\r\n$gdb_prompt $" { pass "break test_adr" } -re "No symbol.*\r\n$gdb_prompt $" { return 0 } } gdb_test "break *test_adr_end" \ "Breakpoint.*at.* file .*$srcfile, line.*" \ "break test_adr_end" gdb_continue_to_breakpoint "test_adr" \ ".*adr.*r0\,.*\.L8.*" gdb_continue_to_breakpoint "test_adr_end" \ ".*bx lr.*" } proc test_adr_32bit {} { global srcfile global gdb_prompt gdb_test_multiple "break *test_adr_32bit" "break test_adr_32bit" { -re "Breakpoint.*at.* file .*$srcfile, line.*\r\n$gdb_prompt $" { pass "break test_adr" } -re "No symbol.*\r\n$gdb_prompt $" { return 0 } } gdb_test "break *test_adr_32bit_after" \ "Breakpoint.*at.* file .*$srcfile, line.*" \ "break test_adr_32bit_after" gdb_test "break *test_adr_32bit_end" \ "Breakpoint.*at.* file .*$srcfile, line.*" \ "break test_adr_32bit_end" gdb_continue_to_breakpoint "test_adr_32bit" \ ".*adr.*r0\,.*\.L6.*" gdb_continue_to_breakpoint "test_adr_32bit_after" \ ".*adr.*r0\,.*\.L6.*" gdb_continue_to_breakpoint "test_adr_32bit_end" \ ".*bx lr.*" } ######################################### # Test pop to PC proc test_pop_pc {} { global srcfile gdb_test "break *test_pop_pc_1" \ "Breakpoint.*at.* file .*$srcfile, line.*" \ "break test_pop_pc_1" gdb_test "break *test_pop_pc_2" \ "Breakpoint.*at.* file .*$srcfile, line.*" \ "break test_pop_pc_2" gdb_test "break *test_pop_pc_3" \ "Breakpoint.*at.* file .*$srcfile, line.*" \ "break test_pop_pc_3" gdb_test "break *test_pop_pc_ret" \ "Breakpoint.*at.* file .*$srcfile, line.*" \ "break test_pop_pc_ret" gdb_test "break *test_pop_pc_1_right" \ "Breakpoint.*at.* file .*$srcfile, line.*" \ "break test_pop_pc_1_right" gdb_test "break *test_pop_pc_1_wrong" \ "Breakpoint.*at.* file .*$srcfile, line.*" \ "break test_pop_pc_1_wrong" gdb_test "break *test_pop_pc_2_right" \ "Breakpoint.*at.* file .*$srcfile, line.*" \ "break test_pop_pc_2_right" gdb_test "break *test_pop_pc_2_wrong" \ "Breakpoint.*at.* file .*$srcfile, line.*" \ "break test_pop_pc_2_wrong" gdb_test "break *test_pop_pc_3_right" \ "Breakpoint.*at.* file .*$srcfile, line.*" \ "break test_pop_pc_3_right" gdb_test "break *test_pop_pc_3_wrong" \ "Breakpoint.*at.* file .*$srcfile, line.*" \ "break test_pop_pc_1_wrong" gdb_continue_to_breakpoint "continue to test_pop_pc_1" \ ".*b.*\{r1\, pc\}.*" gdb_continue_to_breakpoint "continue to test_pop_pc_1_check" \ ".*b.*right.*" gdb_continue_to_breakpoint "continue to test_pop_pc_2" \ ".*\{pc\}.*" gdb_continue_to_breakpoint "continue to test_pop_pc_2_check" \ ".*b.*right.*" gdb_continue_to_breakpoint "continue to test_pop_pc_3" \ ".*\{r0\,r1\,r2\,r3\,r4\,r5\,r6\,r7\,pc\}.*" gdb_continue_to_breakpoint "continue to test_pop_pc_3_check" \ ".*b.*right.*" gdb_continue_to_breakpoint "continue to test_pop_pc_ret" \ ".*r7.*" } ########################################### proc test_str_pc {} { global srcfile global gdb_prompt gdb_test_multiple "break *test_str_pc" "break test_str_pc" { -re "Breakpoint.*at.* file .*$srcfile, line.*\r\n$gdb_prompt $" { pass "break test_str_pc" } -re "No symbol.*\r\n$gdb_prompt $" { pass "break test_str_pc" return } } gdb_test "break *test_str_pc_end" \ "Breakpoint.*at.* file .*$srcfile, line.*" \ "break test_str_pc_end" # Set breakpoint on both lables pc_offset_right and pc_offset_wrong gdb_test "break *pc_offset_right" \ "Breakpoint.*at.* file .*$srcfile, line.*" \ "break pc_offset_right" gdb_test "break *pc_offset_wrong" \ "Breakpoint.*at.* file .*$srcfile, line.*" \ "break pc_offset_wrong" gdb_continue_to_breakpoint "continue to test_str_pc" \ ".*str.*pc\,.*\[sp, #-4\].*" # If breakpoint on lable pc_offset_wrong is hit, that means the offset # computed in displaced stepping is different from offset computed # without displaced stepping. Report a failure. gdb_continue_to_breakpoint "continue to pc_offset_right" \ ".*b.*test_str_pc_end.*" gdb_continue_to_breakpoint "continue to test_str_pc_end" \ ".*bx lr.*" } # Test 16 bit thumb instruction 'add rd, pc'. proc test_add_rn_pc {} { global srcfile gdb_prompt set test "break test_add_rn_pc" gdb_test_multiple "break *test_add_rn_pc" $test { -re "Breakpoint.*at.* file .*$srcfile, line.*\r\n$gdb_prompt $" { pass $test } -re "No symbol.*\r\n$gdb_prompt $" { return } } gdb_continue_to_breakpoint "continue to test_add_rn_pc" \ ".*mov.*r3, 4.*" gdb_test "break *test_add_rn_pc_start" \ "Breakpoint.*at.* file .*$srcfile, line.*" \ "break test_add_rn_pc_start" gdb_continue_to_breakpoint "continue to test_add_rn_pc_start" \ ".*add.*r3,.*pc.*" set pc_val [get_integer_valueof "\$pc" 0] gdb_test "break *test_add_rn_pc_end" \ "Breakpoint.*at.* file .*$srcfile, line.*" \ "break test_add_rn_pc_end" gdb_continue_to_breakpoint "continue to test_add_rn_pc_end" \ ".*bx lr.*" set r3_val [get_integer_valueof "\$r3" 0] # Test the value in r3 is correct. gdb_assert { [expr {$pc_val + 4 + 4} == $r3_val] } } # Turn displaced stepping off before runto main. When displaced stepping # is on, and we type 'run', GDB will first try to single step on _dl_debug_state, # which is in library might be compiled in Thumb. gdb_test_no_output "set displaced-stepping off" if ![runto_main] then { fail "can't run to main" return 0 } gdb_test_no_output "set displaced-stepping on" gdb_test "show displaced-stepping" ".* displaced stepping .* is on.*" test_call_ret test_branch test_ldr_from_pc test_ldm_stm_pc test_ldr_literal test_ldr_literal_16 test_cbz_cbnz test_adr test_adr_32bit test_pop_pc test_str_pc test_add_rn_pc ########################################## # Done, run program to exit. gdb_continue_to_end "arm-disp-step"