diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c index d1e15497a46ca250d606f4da77ad653fecc41e1c..3ac0564dd9a1bf01a7df60059b3a7b353e3b042f 100644 --- a/gdb/aarch64-tdep.c +++ b/gdb/aarch64-tdep.c @@ -3099,15 +3099,22 @@ aarch64_displaced_step_others (const uint32_t insn, struct aarch64_displaced_step_data *dsd = (struct aarch64_displaced_step_data *) data; - aarch64_emit_insn (dsd->insn_buf, insn); - dsd->insn_count = 1; - - if ((insn & 0xfffffc1f) == 0xd65f0000) + uint32_t masked_insn = (insn & CLEAR_Rn_MASK); + if (masked_insn == BLR) { - /* RET */ - dsd->dsc->pc_adjust = 0; + /* Emit a BR to the same register and then update LR to the original + address (similar to aarch64_displaced_step_b). */ + aarch64_emit_insn (dsd->insn_buf, insn & 0xffdfffff); + regcache_cooked_write_unsigned (dsd->regs, AARCH64_LR_REGNUM, + data->insn_addr + 4); } else + aarch64_emit_insn (dsd->insn_buf, insn); + dsd->insn_count = 1; + + if (masked_insn == RET || masked_insn == BR || masked_insn == BLR) + dsd->dsc->pc_adjust = 0; + else dsd->dsc->pc_adjust = 4; } diff --git a/gdb/arch/aarch64-insn.h b/gdb/arch/aarch64-insn.h index 1e8c5eac940ee017f4b6d2685bbcbaeed0617e3a..87e2f5875a133a347c4be48b255a04282647be12 100644 --- a/gdb/arch/aarch64-insn.h +++ b/gdb/arch/aarch64-insn.h @@ -61,7 +61,9 @@ enum aarch64_opcodes CBNZ = 0x21000000 | B, TBZ = 0x36000000 | B, TBNZ = 0x37000000 | B, + /* BR 1101 0110 0001 1111 0000 00rr rrr0 0000 */ /* BLR 1101 0110 0011 1111 0000 00rr rrr0 0000 */ + BR = 0xd61f0000, BLR = 0xd63f0000, /* RET 1101 0110 0101 1111 0000 00rr rrr0 0000 */ RET = 0xd65f0000, @@ -128,6 +130,14 @@ enum aarch64_opcodes NOP = (0 << 5) | HINT, }; +/* List of useful masks. */ +enum aarch64_masks +{ + /* Used for masking out an Rn argument from an opcode. */ + CLEAR_Rn_MASK = 0xfffffc1f, +}; + + /* Representation of a general purpose register of the form xN or wN. This type is used by emitting functions that take registers as operands. */ diff --git a/gdb/testsuite/gdb.arch/insn-reloc.c b/gdb/testsuite/gdb.arch/insn-reloc.c index bfbb3161b3aa3dcf8a5ff00e4341c4d68e473f26..62f13a9275439c8e2abdedd5cfbd88d06d863d03 100644 --- a/gdb/testsuite/gdb.arch/insn-reloc.c +++ b/gdb/testsuite/gdb.arch/insn-reloc.c @@ -512,6 +512,84 @@ can_relocate_bl (void) : : : "x30"); /* Test that LR is updated correctly. */ } +/* Make sure we can relocate a BR instruction. + + ... Set x0 to target + set_point12: + BR x0 ; jump to target (tracepoint here). + fail() + return + target: + pass() + end + + */ + +static void +can_relocate_br (void) +{ + int ok = 0; + + asm goto (" adr x0, %l0\n" + "set_point12:\n" + " br x0\n" + : + : + : "x0" + : madejump); + + fail (); + return; +madejump: + pass (); +} + +/* Make sure we can relocate a BLR instruction. + + We use two different functions since the test runner expects one breakpoint + per function and we want to test two different things. + For BLR we want to test that the BLR actually jumps to the relevant + function, *and* that it sets the LR register correctly. + + Hence we create one testcase that jumps to `pass` using BLR, and one + testcase that jumps to `pass` if BLR has set the LR correctly. + + -- can_relocate_blr_jumps + ... Set x0 to pass + set_point13: + BLR x0 ; jump to pass (tracepoint here). + + -- can_relocate_blr_sets_lr + ... Set x0 to foo + set_point14: + BLR x0 ; jumps somewhere else (tracepoint here). + BL pass ; ensures the LR was set correctly by the BLR. + + */ + +static void +can_relocate_blr_jumps (void) +{ + int ok = 0; + + /* Test BLR indeed jumps to the target. */ + asm ("set_point13:\n" + " blr %[address]\n" + : : [address] "r" (&pass) : "x30"); +} + +static void +can_relocate_blr_sets_lr (void) +{ + int ok = 0; + + /* Test BLR sets the LR correctly. */ + asm ("set_point14:\n" + " blr %[address]\n" + " bl pass\n" + : : [address] "r" (&foo) : "x30"); +} + #endif /* Functions testing relocations need to be placed here. GDB will read @@ -536,6 +614,9 @@ static testcase_ftype testcases[] = { can_relocate_ldr, can_relocate_bcond_false, can_relocate_bl, + can_relocate_br, + can_relocate_blr_jumps, + can_relocate_blr_sets_lr, #endif };