| # Copyright 2010-2021 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/>. |
| |
| # 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" "" { |
| -re "Breakpoint.*at.* file .*$srcfile, line.*\r\n$gdb_prompt $" { |
| pass $gdb_test_name |
| } |
| -re "No symbol.*\r\n$gdb_prompt $" { |
| pass $gdb_test_name |
| 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" "" { |
| -re "Breakpoint.*at.* file .*$srcfile, line.*\r\n$gdb_prompt $" { |
| pass $gdb_test_name |
| } |
| -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" "" { |
| -re "Breakpoint.*at.* file .*$srcfile, line.*\r\n$gdb_prompt $" { |
| pass $gdb_test_name |
| } |
| -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" "" { |
| -re "Breakpoint.*at.* file .*$srcfile, line.*\r\n$gdb_prompt $" { |
| pass $gdb_test_name |
| } |
| -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" "" { |
| -re "Breakpoint.*at.* file .*$srcfile, line.*\r\n$gdb_prompt $" { |
| pass $gdb_test_name |
| } |
| -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" "" { |
| -re "Breakpoint.*at.* file .*$srcfile, line.*\r\n$gdb_prompt $" { |
| pass $gdb_test_name |
| } |
| -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_3_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" "" { |
| -re "Breakpoint.*at.* file .*$srcfile, line.*\r\n$gdb_prompt $" { |
| pass $gdb_test_name |
| } |
| -re "No symbol.*\r\n$gdb_prompt $" { |
| pass $gdb_test_name |
| 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 |
| |
| gdb_test_multiple "break *test_add_rn_pc" "" { |
| -re "Breakpoint.*at.* file .*$srcfile, line.*\r\n$gdb_prompt $" { |
| pass $gdb_test_name |
| } |
| -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 { |
| 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" |