Thanks everyone for the detailed comments.
Let me try to summarize where I think the discussion stands, and then go
through the questions that have emerged, from the most specific ones to the
more general and still unresolved ones.
My RFC originally came from reading QEMU's gdbstub as, at least conceptually,
an analogue of an external low-level debugger such as a JTAG-attached debugger.
From that perspective, having an attached GDB client consume a guest BKPT used
as an external debugger stop point seemed natural.
The discussion has made clear that this assumption is precisely where the issue
lies.
Alex pointed out that:
> "We could treat gdbstub as an external debug monitor but we don't currently
> model it as such."
Richard was more skeptical about whether QEMU should reproduce this behaviour
at all, describing the patch as attempting to replicate a case where a guest
BKPT is trapped by an external debugger, and adding:
> "The only question is whether we want to follow that, and I suspect the
> answer is no."
On the other hand, Mohamed pointed out that this is a typical Cortex-M
debugging pattern, supported by the architectural behaviour of BKPT, and argued
that QEMU's gdbstub matching the behaviour of an OpenOCD-provided GDB stub
would be desirable.
Peter also broadened the issue beyond the Pico case, asking whether breakpoint
instructions are not generally expected to be trapped by an external JTAG
debugger, and expressing surprise that QEMU does not already handle this.
So, at this point, I think the RFC has raised the following questions.
1. Is semihosting an alternative here?
Alex mentioned semihosting as the mechanism normally used when code running on
real hardware requests a service from a JTAG-attached debugger.
I agree that semihosting addresses a related class of interactions, but I do
not think it is an alternative for this particular problem, as Alex also
qualified with "not for breakpoints".
The goal here is precisely to run existing firmware unchanged. The Pico SDK
already deliberately executes BKPT in its exit/debug path, and replacing that
mechanism with semihosting would change the guest software rather than
reproduce its existing behaviour.
So I think this particular question is mostly resolved: semihosting is relevant
context, but not a transparent replacement for guest breakpoint instructions
already present in existing firmware.
2. Is this problem specific to the Pico, or even to Arm M-profile?
I initially encountered the problem while working on RP2040 support, but the
discussion strongly suggests that the underlying issue is more general.
I had already noted the analogous use of EBREAK on RISC-V, for example on
RP2350 firmware.
Mohamed also described the x86 case, where QEMU distinguishes different kinds
of breakpoint instructions and traps guest debug exceptions when GDB is
attached, while noting the additional problem of reinjecting exceptions that
are not intended for the external debugger.
Peter's comment also points in this direction:
> "But aren't breakpoint instructions of all kinds typically trapped to the
> external jtag debugger, on any architecture ? I'm a bit surprised that we
> don't do this already."
So my current understanding is that the problem should probably not be framed
as an Arm M-profile-specific one.
If that is correct, then my current patch is addressing the issue at the wrong
abstraction level by adding Arm-specific knowledge directly in
target/arm/tcg/debug.c. I was in fact already expecting to face the same
question for RISC-V EBREAK.
This would suggest that QEMU may instead need a more explicitly defined generic
policy for guest breakpoint instructions executed while an external GDB client
is attached, with architecture-specific code only deciding whether a particular
debug event is eligible for external interception.
3. Which architectural state should control the interception?
Even if we agree that a guest breakpoint instruction may be consumed by an
external debugger, my original condition is clearly too simple:
```
arm_feature(env, ARM_FEATURE_M) && gdb_cpu_is_attached(cs)
```
Alex explicitly mentioned DHCSR, DEMCR, and IMPDEF behaviour.
Mohamed cited, in particular, the architectural role of DHCSR.C_DEBUGEN and the
Armv8-M rule that BKPT may lead either to debug halt, DebugMonitor, HardFault,
or lockup depending on the debug configuration.
So the technical question is not merely whether a GDB client is attached. It is
also which architectural state QEMU should take into account when deciding
whether the breakpoint is consumed by the external debugger.
For Arm M-profile, this seems to include at least questions around:
* DHCSR.C_DEBUGEN;
* whether halting debug is enabled or permitted in the current processor state;
* DEMCR and DebugMonitor configuration;
* possible architecture-version-specific or IMPDEF behaviour.
This raises another conceptual question: should attaching GDB to QEMU merely
make an external debugger available, or should it also imply some architectural
debug configuration equivalent to what a real debug probe would establish?
4. How should self-hosted debugging be preserved?
This seems to me the main practical difficulty.
Peter explicitly pointed out that a guest breakpoint instruction may belong to
a debugger running inside the guest itself.
Mohamed's x86 example makes the problem particularly clear: QEMU may first
intercept a debug exception while GDB is attached, but if that exception is not
intended for the external debugger, it must remain visible to the guest.
Mohamed also mentioned that there is currently a bug in the x86 path where some
such exceptions are ignored instead of reinjected.
So perhaps the real question is not simply:
```
external debugger or guest exception?
```
but rather:
```
Should the external debugger get a first chance to consume the debug event,
and if it does not, should the event be reinjected into the guest?
```
For Cortex-M, a rule that simply maps every BKPT to EXCP_DEBUG whenever GDB is
attached could clearly steal an exception that should have remained
guest-visible.
This means that any generic abstraction would need to define not only when an
external debugger may intercept a guest breakpoint instruction, but also what
happens when it does not consume it.
5. Should QEMU's gdbstub be modelled as an external debug monitor?
This now seems to me to be the most general question behind the RFC.
If the answer is yes, then some variant of the behaviour I proposed seems
natural: a breakpoint instruction that would be consumed by an external
debugger on real hardware should be reportable to an attached GDB client.
If the answer is no, then I think we need to clarify what the intended
relationship is between QEMU's gdbstub and the architecture's external debug
facilities.
Alex's remark captures the current situation well:
> "We could treat gdbstub as an external debug monitor but we don't currently
> model it as such."
So perhaps the main remaining design question is where this abstraction should
live.
Should target-specific code directly know whether GDB is attached, as in my RFC?
Or should there be a generic QEMU debug layer representing an external
debugger, with target code only reporting architecture-specific debug events
and deciding, from architectural state, whether they are eligible for external
interception?
The latter seems cleaner to me, especially if the same issue exists for Arm
BKPT, RISC-V EBREAK, and breakpoint instructions on other architectures.
My current feeling is therefore that the original RFC probably puts the fix too
low and too specifically in the Arm code, but that the underlying problem
remains real: QEMU does not appear to have a clearly defined generic model for
what should happen when guest code deliberately executes a breakpoint
instruction while an external GDB client is attached.
----
I have tried to summarize the discussion as faithfully as possible, as I was
beginning to lose track of the different points and positions.
This leaves me with one final question, which may actually be the key to
choosing between the possible models discussed above.
Even if you decide that QEMU's gdbstub should represent an external debugger,
what exactly should that imply?
Does attaching a GDB client itself mean that architectural external halting
debug is active, so that eligible guest breakpoint instructions should
naturally be consumed by the external debugger?
Or does it only mean that an external debugger is available, while actual
interception must still depend on the architectural debug state, such as
DHCSR.C_DEBUGEN and the relevant architecture-specific configuration?
My original patch implicitly assumes the first model:
GDB attached
=> external halting debug active
=> M-profile BKPT is reported to GDB
But the discussion around DHCSR, DEMCR, self-hosted debugging, and the role of
the gdbstub suggests that this equivalence may be exactly what needs to be
decided first.
Perhaps answering this question would also tell us where the abstraction should
live: either directly in target-specific exception handling, or in a more
generic QEMU external-debug layer with architecture-specific eligibility rules.
Have I missed an important case or misunderstood the emerging consensus here?
Thanks again for the discussion,
--
Gilles
> Le 8 juil. 2026 à 12:10, Mohamed Mediouni <[email protected]> a écrit :
>
>
>
>> On 8. Jul 2026, at 11:31, Peter Maydell <[email protected]> wrote:
>>
>> On Tue, 7 Jul 2026 at 18:27, Richard Henderson
>> <[email protected]> wrote:
>>>
>>> On 7/6/26 02:21, Peter Maydell wrote:
>>>> No other architecture, CPU or board in QEMU needs to do this,
>>>> so my instinct is to say that M-profile should not be special here.
>>>> Richard, Alex: how do we usually handle breakpoint insns for the
>>>> gdbstub ?
>>>
>>> We handle them via CPUBreakpoint structures; we never inject code changes.
>>
>> I don't mean "breakpoints that the gdbstub requests", I mean
>> "breakpoint insns that are already in the guest code that get
>> executed".
>>
>>> The patch description explains the confusion: this is attempting to
>>> replicate a feature of
>>> the Raspberry Pi Pico SDK, wherein a guest BPKT really is trapped by an
>>> external debugger.
>>
>> But aren't breakpoint instructions of all kinds typically trapped
>> to the external jtag debugger, on any architecture ? I'm a bit
>> surprised that we don't do this already.
>
> Some context on the gdbstub implementation from the x86 side:
>
> WHPX's gdbstub support is implemented with overriding the instruction
> instead of leveraging HW breakpoints - although a bit hackily.
>
> A big concern is still allowing self-hosted debug to work fine which
> makes things a bit harder.
>
> x86 makes things easier by having multiple different breakpoint instructions.
> Linux uses int3 and we use int1* for QEMU gdbstub-controlled breakpoints so
> that things (mostly) work out for everyone.
>
> And then, QEMU traps all debug exceptions from the guest when gdb is attached
> and is supposed to take action when it’s an int1 breakpoint and not others.
>
> Although we currently have a bug there that the non int1s just get ignored
> instead of being injected back.
>
> * on x86, the Intel manual says "Hardware vendors may use the INT1
> instruction for hardware debug"
>
> On Cortex-M self-hosted debug is far less used than for emulating bigger
> chips.
>
> And then there’s emulating such a thing using hardware virt and using HW
> breakpoints
> which quickly becomes quite complicated...