diff options
Diffstat (limited to 'gdb/arch/arm-get-next-pcs.c')
-rw-r--r-- | gdb/arch/arm-get-next-pcs.c | 143 |
1 files changed, 120 insertions, 23 deletions
diff --git a/gdb/arch/arm-get-next-pcs.c b/gdb/arch/arm-get-next-pcs.c index 6c18236..82b077c 100644 --- a/gdb/arch/arm-get-next-pcs.c +++ b/gdb/arch/arm-get-next-pcs.c @@ -1,6 +1,6 @@ /* Common code for ARM software single stepping support. - Copyright (C) 1988-2024 Free Software Foundation, Inc. + Copyright (C) 1988-2025 Free Software Foundation, Inc. This file is part of GDB. @@ -39,10 +39,92 @@ arm_get_next_pcs_ctor (struct arm_get_next_pcs *self, self->regcache = regcache; } -/* Checks for an atomic sequence of instructions beginning with a LDREX{,B,H,D} - instruction and ending with a STREX{,B,H,D} instruction. If such a sequence - is found, attempt to step through it. The end of the sequence address is - added to the next_pcs list. */ +/* Return true if INSN matches one of the <value,mask> pairs in MATCHES. */ + +static bool +insn_matches (uint32_t insn, gdb::array_view<const uint32_t> matches) +{ + gdb_assert (matches.size () % 2 == 0); + + for (int i = 0; i < matches.size (); i += 2) + { + uint32_t value = matches[i]; + uint32_t mask = matches[i + 1]; + if ((insn & mask) == value) + return true; + } + + return false; +} + +/* Return true if INSN is an ldaex arm insn. */ + +static bool +ldaex_arm_p (uint32_t insn) +{ + /* Copied from arm_opcodes in opcodes/arm-dis.c. */ + const uint32_t matches_ldaex_arm[] = { + 0x01900e9f, 0x0ff00fff, /* ldaex. */ + 0x01b00e9f, 0x0ff00fff, /* ldaexd. */ + 0x01d00e9f, 0x0ff00fff, /* ldaexb. */ + 0x01f00e9f, 0x0ff00fff /* ldaexh. */ + }; + + return insn_matches (insn, matches_ldaex_arm); +} + +/* Return true if INSN is an ldaex thumb32 insn. */ + +static bool +ldaex_thumb32_p (uint32_t insn) +{ + /* Copied from thumb32_opcodes in opcodes/arm-dis.c. */ + const uint32_t matches_ldaex_thumb32[] = { + 0xe8d00fcf, 0xfff00fff, /* ldaexb. */ + 0xe8d00fdf, 0xfff00fff, /* ldaexh. */ + 0xe8d00fef, 0xfff00fff, /* ldaex. */ + 0xe8d000ff, 0xfff000ff /* ldaexd. */ + }; + + return insn_matches (insn, matches_ldaex_thumb32); +} + +/* Return true if INSN is an stlex arm insn. */ + +static bool +stlex_arm_p (uint32_t insn) +{ + /* Copied from arm_opcodes in opcodes/arm-dis.c. */ + const uint32_t matches_stlex_arm[] = { + 0x01800e90, 0x0ff00ff0, /* stlex. */ + 0x01a00e90, 0x0ff00ff0, /* stlexd. */ + 0x01c00e90, 0x0ff00ff0, /* stlexb. */ + 0x01e00e90, 0x0ff00ff0, /* stlexh. */ + }; + + return insn_matches (insn, matches_stlex_arm); +} + +/* Return true if INSN is an stlex thumb32 insn. */ + +static bool +stlex_thumb32_p (uint32_t insn) +{ + /* Copied from thumb32_opcodes in opcodes/arm-dis.c. */ + const uint32_t matches_stlex_thumb32[] = { + 0xe8c00fc0, 0xfff00ff0, /* stlexb. */ + 0xe8c00fd0, 0xfff00ff0, /* stlexh. */ + 0xe8c00fe0, 0xfff00ff0, /* stlex. */ + 0xe8c000f0, 0xfff000f0, /* stlexd. */ + }; + + return insn_matches (insn, matches_stlex_thumb32); +} + +/* Checks for an atomic sequence of instructions beginning with an + LD[AR]EX{,B,H,D} instruction and ending with a ST[LR]EX{,B,H,D} instruction. + If such a sequence is found, attempt to step through it. The end of the + sequence address is added to the next_pcs list. */ static std::vector<CORE_ADDR> thumb_deal_with_atomic_sequence_raw (struct arm_get_next_pcs *self) @@ -64,18 +146,22 @@ thumb_deal_with_atomic_sequence_raw (struct arm_get_next_pcs *self) if (itstate & 0x0f) return {}; - /* Assume all atomic sequences start with a ldrex{,b,h,d} instruction. */ insn1 = self->ops->read_mem_uint (loc, 2, byte_order_for_code); - loc += 2; + if (thumb_insn_size (insn1) != 4) return {}; insn2 = self->ops->read_mem_uint (loc, 2, byte_order_for_code); - loc += 2; + + uint32_t insn = (uint32_t)insn2 | ((uint32_t)insn1 << 16); + + /* Assume all atomic sequences start with an ld[ar]ex{,b,h,d} + instruction. */ if (!((insn1 & 0xfff0) == 0xe850 - || ((insn1 & 0xfff0) == 0xe8d0 && (insn2 & 0x00c0) == 0x0040))) + || ((insn1 & 0xfff0) == 0xe8d0 && (insn2 & 0x00c0) == 0x0040) + || ldaex_thumb32_p (insn))) return {}; /* Assume that no atomic sequence is longer than "atomic_sequence_length" @@ -110,9 +196,10 @@ thumb_deal_with_atomic_sequence_raw (struct arm_get_next_pcs *self) else { insn2 = self->ops->read_mem_uint (loc, 2, byte_order_for_code); - loc += 2; + insn = (uint32_t)insn2 | ((uint32_t)insn1 << 16); + /* Assume that there is at most one conditional branch in the atomic sequence. If a conditional branch is found, put a breakpoint in its destination address. */ @@ -147,9 +234,10 @@ thumb_deal_with_atomic_sequence_raw (struct arm_get_next_pcs *self) else if (thumb2_instruction_changes_pc (insn1, insn2)) return {}; - /* If we find a strex{,b,h,d}, we're done. */ + /* If we find a st[lr]ex{,b,h,d}, we're done. */ if ((insn1 & 0xfff0) == 0xe840 - || ((insn1 & 0xfff0) == 0xe8c0 && (insn2 & 0x00c0) == 0x0040)) + || ((insn1 & 0xfff0) == 0xe8c0 && (insn2 & 0x00c0) == 0x0040) + || stlex_thumb32_p (insn)) break; } } @@ -177,10 +265,10 @@ thumb_deal_with_atomic_sequence_raw (struct arm_get_next_pcs *self) return next_pcs; } -/* Checks for an atomic sequence of instructions beginning with a LDREX{,B,H,D} - instruction and ending with a STREX{,B,H,D} instruction. If such a sequence - is found, attempt to step through it. The end of the sequence address is - added to the next_pcs list. */ +/* Checks for an atomic sequence of instructions beginning with an + LD[AR]EX{,B,H,D} instruction and ending with a ST[LR]EX{,B,H,D} instruction. + If such a sequence is found, attempt to step through it. The end of the + sequence address is added to the next_pcs list. */ static std::vector<CORE_ADDR> arm_deal_with_atomic_sequence_raw (struct arm_get_next_pcs *self) @@ -195,13 +283,16 @@ arm_deal_with_atomic_sequence_raw (struct arm_get_next_pcs *self) int last_breakpoint = 0; /* Defaults to 0 (no breakpoints placed). */ const int atomic_sequence_length = 16; /* Instruction sequence length. */ - /* Assume all atomic sequences start with a ldrex{,b,h,d} instruction. - Note that we do not currently support conditionally executed atomic - instructions. */ insn = self->ops->read_mem_uint (loc, 4, byte_order_for_code); - loc += 4; - if ((insn & 0xff9000f0) != 0xe1900090) + + /* Currently we do not support conditionally executed atomic instructions. */ + if (!insn_condition_always_true (insn)) + return {}; + + /* Assume all atomic sequences start with an ld[ar]ex{,b,h,d} instruction. */ + if (!((insn & 0x0f9000f0) == 0x01900090 + || ldaex_arm_p (insn))) return {}; /* Assume that no atomic sequence is longer than "atomic_sequence_length" @@ -231,8 +322,14 @@ arm_deal_with_atomic_sequence_raw (struct arm_get_next_pcs *self) else if (arm_instruction_changes_pc (insn)) return {}; - /* If we find a strex{,b,h,d}, we're done. */ - if ((insn & 0xff9000f0) == 0xe1800090) + /* Currently we do not support conditionally executed atomic + instructions. */ + if (!insn_condition_always_true (insn)) + return {}; + + /* If we find a st[lr]ex{,b,h,d}, we're done. */ + if ((insn & 0x0f9000f0) == 0x01800090 + || stlex_arm_p (insn)) break; } |