cancel
Showing results for 
Search instead for 
Did you mean: 

GDB hook-stop not triggered when next/step is interrupted by a software breakpoint (__BKPT)

parmi93
Associate II

I am reporting a specific behavior in GDB where the standard hook-stop is completely bypassed if a next (Step Over (F10)) or step (Step Into (F11)) command is interrupted by a software breakpoint instruction (BKPT) embedded within a macro.

Environment:

  • Target: STM32L475VGT (ST-Link)

  • GDB Version: GNU gdb (GNU Tools for STM32 14.3.rel1.20251027-0700) 15.2.90.20241229-git

  • IDE: STM32CubeIDE for VSCode v3.9.0

Steps to Reproduce:

1. Define a macro in C that includes a software breakpoint instruction:

#define DEBUGGER_ATTACHED() ((CoreDebug->DHCSR & CoreDebug_DHCSR_C_DEBUGEN_Msk) != 0)

#define BREAKPOINT(arg)                    \
    do {                                   \
        if (DEBUGGER_ATTACHED()) {         \
            __BKPT(arg);                   \
            __NOP(); /* NOP to prevent the debugger from immediately stepping to the next line. */ \
        }                                  \
    } while (0)

int main() {
    int a = 0;        // Click: Continue (F5)
    BREAKPOINT(0x01); // Click: Step Over (F10) which works correctly
    a++;              // Click: Step Over (F10) again which works correctly
    BREAKPOINT(0x01); // Click: Step Over (F10) and the IDE will end-up in a "limbo"
    a++;
}

2. In the .gdbinit file, define the following hooks:

define hook-next
  echo ---> hook-next\n
end

define hookpost-next
  echo ---> hookpost-next\n
end

define hook-stop
  echo ---> hook-stop\n

  # Detect ARM Thumb __BKPT instruction
  if (*(unsigned char*)($pc+1)) == 0xBE
    # Read __BKPT argument
    set $arg = *(unsigned char*)($pc)

    if $arg == 0x01
      # Skip the BKPT instruction (16-bit); otherwise, the PC will remain halted on the breakpoint 
      # instruction, preventing the user from performing step-over, step-into, continue, etc.
      set $pc = $pc + 2
      echo ---> __BKPT(0x01) handled\n
    end
  end
end

3. Start a debug session and perform a Step Over (F10) (next) on the line where the second BREAKPOINT(0x01) is invoked.

Observed Behavior:
When performing a next over a "normal" line of code (like a++), the execution sequence is:
1. hook-next
2. next (actual command)
3. hookpost-next
4. hook-stop <-- Triggered correctly

However, when performing next over the second BREAKPOINT(0x01) macro:
1. hook-next
2. next
3. hookpost-next
4. [MISSING] hook-stop is never executed.

As a result, GDB enters a state of "limbo". In IDEs like VS Code, the debugger is reported as "Running" (as it is still waiting for the macro BREAKPOINT(0x01) to finish the execution), but the CPU is actually halted. The user is forced to manually stop (Pause (F6)) the session to regain control which invokes hook-stop.

Technical Analysis:
It appears that when next/step operation is performed over a macro GDB treats it as an atomic transaction. But if the CPU hits a BKPT instruction before reaching the destination address calculated by next/step, GDB receives a SIGTRAP. However, instead of finalizing the stop sequence and triggering hook-stop, GDB seems to abort the transaction. Since the next command didn't "finish" successfully at the expected PC (program counter), the hook-stop is bypassed, even though the target is effectively halted.

This creates a significant issue for automated scripts that rely on hook-stop to manage Program Counter (PC) adjustments (e.g., skipping the 2-byte BKPT instruction).

Expected Behavior:
GDB should trigger hook-stop whenever the target halts, regardless of whether the halt was caused by the completion of a stepping command or an unexpected software breakpoint (BKPT) instruction encountered during that step.

 

0 REPLIES 0