0

I have a pretty trivial bit of bare-metal assembly code running on an arm64 QEMU instance. When debugging with GDB via the QEMU debug port, single step (stepi) is advancing over instructions rather than advancing per line of assembly. The pattern seems to be that it advances directly to the next branch instruction or branch target. The code being advancing over definitely is executed as the register side-effects are visible.

For example, the following code when stepped through (stepi), only stops on the following highlighted lines which are either branches or branch targets, however, x2 is clearly incremented:

    ldr x0, =0x08000000
    ldr x3, =-1

loop:
    ldxr x2, [x0]          <<< GDB "stepi" stops here
    add x2, x2, #1         <<< skipped
    stxr w3, x2, [x0]      <<< skipped
    b trampoline           <<< GDB "stepi" stops here
    nop

trampoline:
    b loop                 <<< GDB "stepi" stops here

This smells on the surface like missing/incomplete debug info in the .elf file, but i've tried every gcc/as -g option I am aware of. I haven't experienced this behavior when running GDB natively on a userspace application, so wondering if this is a QEMU oddity.

  • Yes, rather than "_like missing/incomplete debug info in the .elf file_", it smells like an error in QEMU, since there's no debug info needed for machine instruction stepping. Maybe there's an answer to [How to single step ARM assembly in GDB on QEMU?](https://stackoverflow.com/questions/20590155/how-to-single-step-arm-assembly-in-gdb-on-qemu) – Armali Jun 04 '20 at 06:22
  • Which version of QEMU are you using? You could try with the most recent to see if it is a bug that's been fixed. – Peter Maydell Jun 04 '20 at 09:53

1 Answers1

2

Not an error in qemu, gdb does this on purpose.

ldrx ... strx is an atomic memory access monitor operation (read ARM assembly for detail). If gdb steps through each of these instruction as usual (gdb in the background, use store operation to set breakpoint INSTR, and later restore the original instruction -- another store op), then the hardware will assert ldrx .../strx atomic load and store is not achieved, due to somewhere (i.e., the debugger), another store operation is made in the meantime.

If the assembly code then checks if the strx is really atomic with respect to ldrx and retry if not (which your code does not do, but typically is done in software), then, the hardware will never assert atomic access is established. Stepping these code with retry will fall into a forever loop.

To overcome the artifacts, gdb stepi skip the atomic session (from ldrx to strx sequence) as if they are a single instruction.

quadcore
  • 21
  • 2
  • It seem to me I had the opposite problem: `gdb` was stepping through all of these instructions and thus `stlxr` was always failing and the loop could never complete. I had to manually add breakpoint in the line below the loop, and use `continue` to get over it. Your answer seems to explain the behavior, thank you! – qbolec Feb 06 '22 at 18:34
  • GDB handles strex{,b,h,d} only. Also if strex is 16 instruction away from ldrx, it will not handle. if there are more than one conditional branches in between ldrx and strx it will not handle. if there is only one conditional branch, GDB will plant breakpoint at branch taken landing address to help your figure out if it is taken. if this address is still in the ldrx and strx region (uncommon), even if it is not taken, the atomic attribute is still compromised due to the gdb hidden work. – quadcore Feb 09 '22 at 19:23