From: Tomas Vanek <vanekt@fbl.cz>
To: Simon Marchi <simark@simark.ca>, gdb-patches@sourceware.org
Subject: Re: [PATCH v2] gdb: Modify until_break_command to act correctly on SIGTRAMP_FRAME
Date: Tue, 10 Jan 2023 17:33:42 +0100 [thread overview]
Message-ID: <c6e692a7-2b67-1e97-223d-31a3395d63bf@fbl.cz> (raw)
In-Reply-To: <f0f8828a-2a46-aa94-e42c-985d06b7732b@simark.ca>
On 10/01/2023 16:31, Simon Marchi wrote:
>
> On 10/21/22 07:58, Tomas Vanek wrote:
>> This patch partially depends on
>> gdb/arm: Terminate frame unwinding in M-profile lockup state
>> (without it lockup state is unwound as if it were a normal
>> stack frame).
>>
>> The commands 'advance' and 'until' try to set a breakpoint
>> on the bogus return address derived from Arm M-profile magic
>> address (actually EXC_RETURN or a PC value indicating lockup).
>>
>> The offending breakpoint should be set at the return address in
>> the caller. The magic value 0xffffffff in LR indicates
>> there is no caller (return to this address would lock up the CPU).
>>
>> Similar behaviour of 'advance' and 'until' is observed in
>> an exception handler routine. In this case LR contains e.g.
>> 0xfffffff1 (EXC_RETURN) and GDB tries to set a breakpoint at
>> 0xfffffff0. It should use a return value stacked by the exception
>> instead.
>>
>> Testbench setup:
>> STM32G474, a Cortex-M4 device. Any Cortex-M device can be used.
>> A test application (an ordinary blink) with a standard startup
>> is loaded to the device flash.
>>
>> Steps to reproduce the problem:
>>
>> start GDB server
>> $ openocd -f interface/cmsis-dap.cfg -f target/stm32g4x.cfg
>>
>> start GDB in second terminal
>> $ arm-none-eabi-gdb blink.elf
>>
>> (gdb) target extended-remote localhost:3333
>>
>> Reset the device and halt it:
>> (gdb) monitor reset halt
>> target halted due to debug-request, current mode: Thread
>> xPSR: 0x01000000 pc: 0x08000e14 msp: 0x20020000
>>
>> Step by one instruction to re-read GDB register cache:
>> (gdb) stepi
>>
>> Check registers, LR should be 0xffffffff after reset:
>> (gdb) info registers
>> ...
>> sp 0x20020000 0x20020000
>> lr 0xffffffff -1
>> pc 0x8000e16 0x8000e16
>> xPSR 0x1000000 16777216
>> ...
>>
>> (gdb) set debug remote
>>
>> Issue 'advance' command:
>> (gdb) advance main
>> [remote] Sending packet: $mfffffffe,2#fa
>> [remote] Packet received: 0000
>> [remote] Sending packet: $mfffffffe,2#fa
>> [remote] Packet received: 0000
>> [remote] Sending packet: $m8000526,2#30
>> [remote] Packet received: 2046
>> [remote] Sending packet: $Z1,8000526,2#7a
>> [remote] Packet received: OK
>> [remote] packet_ok: Packet Z1 (hardware-breakpoint) is supported
>> [remote] Sending packet: $Z0,fffffffe,2#43
>> [remote] Packet received: E0E
>> [remote] packet_ok: Packet Z0 (software-breakpoint) is supported
>> Warning:
>> Cannot insert breakpoint 0.
>> Cannot access memory at address 0xfffffffe
>>
>> Command aborted.
>> (gdb)
>>
>> Relevant messages from OpenOCD:
>> Error: Failed to read memory at 0xfffff000
>> Error: can't add breakpoint: unknown reason
>>
>> This patch adds skipping over frames that are not suitable for
>> guarding with a breakpoint inspired by 'finish' command.
>> If no suitable frame is found, a momentary breakpoint is not set.
>>
>> v2: Comment fixes, bug reference.
>>
>> Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=28683
>> Signed-off-by: Tomas Vanek <vanekt@fbl.cz>
> Hi Tomas,
>
> In order to better understand if this is the right fix, can you describe
> what the different frames are in this situation, starting with the
> current frame? I'd like to see what the frame ids and frame types are.
>
> Is there any chance this could be reproduced using the GDB simulator (or maybe
> qemu), so I could tinker with it?
>
> Simon
Hi Simon,
I'm not familiar with GDB sim nor qemu and have no idea if their
Cortex-M profile
implementation is precise enough to get the same behaviour. I'll give it
a try...
Please see gdb debug output, I marked compute_frame_id results. This is
a real Cortex-M device (this time STM32H7A3) right after reset,
OpenOCD used as gdb server:
(gdb) maintenance flush register-cache
Register cache flushed.
(gdb) set debug frame 1
(gdb) bt -past-entry
[frame] get_prev_frame_always_1: enter
[frame] get_prev_frame_always_1: this_frame=-1
[frame] get_prev_frame_always_1: ->
{level=0,type=<unknown>,unwinder=<unknown>,pc=0x800609c,id=<not
computed>,func=<unknown>} // cached
[frame] get_prev_frame_always_1: exit
[frame] get_prev_frame_always_1: enter
[frame] get_prev_frame_always_1: this_frame=-1
[frame] get_prev_frame_always_1: ->
{level=0,type=<unknown>,unwinder=<unknown>,pc=0x800609c,id=<not
computed>,func=<unknown>} // cached
[frame] get_prev_frame_always_1: exit
[frame] compute_frame_id: enter
[frame] compute_frame_id: fi=0
[frame] frame_unwind_find_by_frame: enter
[frame] frame_unwind_find_by_frame: this_frame=0
[frame] frame_unwind_try_unwinder: trying unwinder "dummy"
[frame] frame_unwind_try_unwinder: no
[frame] frame_unwind_try_unwinder: trying unwinder "dwarf2 tailcall"
[frame] frame_unwind_try_unwinder: no
[frame] frame_unwind_try_unwinder: trying unwinder "inline"
[frame] frame_unwind_try_unwinder: no
[frame] frame_unwind_try_unwinder: trying unwinder "jit"
[frame] frame_unwind_try_unwinder: no
[frame] frame_unwind_try_unwinder: trying unwinder "arm m exception
lockup sec_fnc"
[frame] frame_unwind_try_unwinder: no
[frame] frame_unwind_try_unwinder: trying unwinder "arm stub"
[frame] frame_unwind_try_unwinder: no
[frame] frame_unwind_try_unwinder: trying unwinder "dwarf2"
[frame] frame_unwind_try_unwinder: no
[frame] frame_unwind_try_unwinder: trying unwinder "dwarf2 signal"
[frame] frame_unwind_try_unwinder: no
[frame] frame_unwind_try_unwinder: trying unwinder "arm exidx"
[frame] frame_unwind_try_unwinder: no
[frame] frame_unwind_try_unwinder: trying unwinder "arm epilogue"
[frame] frame_unwind_register_value: enter
[frame] frame_unwind_register_value: frame=-1, regnum=25(xPSR)
[frame] frame_unwind_register_value: -> register=25
bytes=[00000001]
[frame] frame_unwind_register_value: exit
[frame] frame_unwind_try_unwinder: no
[frame] frame_unwind_try_unwinder: trying unwinder "arm prologue"
[frame] frame_unwind_try_unwinder: yes
[frame] frame_unwind_find_by_frame: exit
[frame] frame_unwind_register_value: enter
[frame] frame_unwind_register_value: frame=-1, regnum=13(sp)
[frame] frame_unwind_register_value: -> register=13 bytes=[00000220]
[frame] frame_unwind_register_value: exit
[frame] frame_unwind_register_value: enter
[frame] frame_unwind_register_value: frame=-1, regnum=91(msp)
[frame] frame_unwind_register_value: -> register=91 bytes=[00000220]
[frame] frame_unwind_register_value: exit
[frame] frame_unwind_register_value: enter
[frame] frame_unwind_register_value: frame=-1, regnum=92(psp)
[frame] frame_unwind_register_value: -> register=92 bytes=[00000000]
[frame] frame_unwind_register_value: exit
[frame] frame_unwind_register_value: enter
[frame] frame_unwind_register_value: frame=-1, regnum=25(xPSR)
[frame] frame_unwind_register_value: -> register=25 bytes=[00000001]
[frame] frame_unwind_register_value: exit
[frame] frame_unwind_register_value: enter
[frame] frame_unwind_register_value: frame=-1, regnum=13(sp)
[frame] frame_unwind_register_value: -> register=13 bytes=[00000220]
[frame] frame_unwind_register_value: exit
[frame] get_frame_func_if_available: this_frame=0 -> 0x800609c
[frame] frame_id_p:
l={stack=0x20020000,code=0x000000000800609c,!special} -> 1
[frame] compute_frame_id: ->
{stack=0x20020000,code=0x000000000800609c,!special} <------- *current frame*
[frame] compute_frame_id: exit
#0 Reset_Handler ([frame] frame_id_p: l={!stack,!code,!special} -> 0
) at startup_stm32h7a3xx.s:62
[frame] operator==:
l={stack=0x20020000,code=0x000000000800609c,!special},
r={!stack,!code,!special} -> 0
[frame] get_prev_frame: enter
[frame] get_prev_frame_always_1: enter
[frame] get_prev_frame_always_1: this_frame=0
[frame] get_prev_frame_raw: ->
{level=1,type=<unknown>,unwinder=<unknown>,pc=<unknown>,id=<not
computed>,func=<unknown>}
[frame] compute_frame_id: enter
[frame] compute_frame_id: fi=1
[frame] frame_unwind_find_by_frame: enter
[frame] frame_unwind_find_by_frame: this_frame=1
[frame] frame_unwind_arch: next_frame=0 -> armv7e-m
[frame] frame_unwind_try_unwinder: trying unwinder "dummy"
[frame] frame_unwind_try_unwinder: no
[frame] frame_unwind_try_unwinder: trying unwinder "dwarf2
tailcall"
[frame] frame_unwind_try_unwinder: no
[frame] frame_unwind_try_unwinder: trying unwinder "inline"
[frame] frame_unwind_register_value: enter
[frame] frame_unwind_register_value: frame=0, regnum=15(pc)
[frame] frame_unwind_register_value: enter
[frame] frame_unwind_register_value: frame=0, regnum=14(lr)
[frame] frame_id_p:
l={stack=<sentinel>,!code,special=0x0000000000000000} -> 1
[frame] frame_id_p:
l={stack=<sentinel>,!code,special=0x0000000000000000} -> 1
[frame] operator==:
l={stack=<sentinel>,!code,special=0x0000000000000000},
r={stack=<sentinel>,!code,special=0x0000000000000000} -> 1
[frame] frame_unwind_register_value: enter
[frame] frame_unwind_register_value: frame=-1, regnum=14(lr)
[frame] frame_unwind_register_value: -> register=14
bytes=[ffffffff]
[frame] frame_unwind_register_value: exit
[frame] frame_id_p:
l={stack=<sentinel>,!code,special=0x0000000000000000} -> 1
[frame] operator==:
l={stack=<sentinel>,!code,special=0x0000000000000000},
r={stack=<sentinel>,!code,special=0x0000000000000000} -> 1
[frame] get_prev_frame_always_1: enter
[frame] get_prev_frame_always_1: this_frame=-1
[frame] get_prev_frame_always_1: ->
{level=0,type=NORMAL_FRAME,unwinder="arm
prologue",pc=0x800609c,id={stack=0x20020000,code=0x000000000800609c,!special},func=0x800609c}
//
cached
[frame] get_prev_frame_always_1: exit
[frame] value_fetch_lazy_register: (frame=0, regnum=14(lr),
...) -> register=14 bytes=[ffffffff]
[frame] frame_unwind_register_value: -> register=14
bytes=[ffffffff]
[frame] frame_unwind_register_value: exit
[frame] frame_unwind_register_value: -> computed
bytes=[ffffffff]
[frame] frame_unwind_register_value: exit
[frame] frame_unwind_pc: this_frame=0 -> 0xffffffff
[frame] frame_unwind_try_unwinder: no
[frame] frame_unwind_try_unwinder: trying unwinder "jit"
[frame] frame_unwind_try_unwinder: no
[frame] frame_unwind_try_unwinder: trying unwinder "arm m
exception lockup sec_fnc"
[frame] frame_unwind_try_unwinder: yes
[frame] frame_unwind_find_by_frame: exit
[frame] frame_unwind_register_value: enter
[frame] frame_unwind_register_value: frame=0, regnum=13(sp)
[frame] frame_unwind_register_value: -> computed bytes=[00000220]
[frame] frame_unwind_register_value: exit
[frame] frame_unwind_register_value: enter
[frame] frame_unwind_register_value: frame=0, regnum=91(msp)
[frame] frame_unwind_register_value: -> computed bytes=[00000220]
[frame] frame_unwind_register_value: exit
[frame] frame_unwind_register_value: enter
[frame] frame_unwind_register_value: frame=0, regnum=92(psp)
[frame] frame_unwind_register_value: -> computed bytes=[00000000]
[frame] frame_unwind_register_value: exit
[frame] frame_id_p:
l={stack=0x0,code=0x00000000ffffffff,!special} -> 1
[frame] compute_frame_id: ->
{stack=0x0,code=0x00000000ffffffff,!special} <----- *magic value
0xffffffff in LR indicates there is no caller*
[frame] compute_frame_id: exit
[frame] get_prev_frame_always_1: exit
[frame] get_prev_frame: exit
#1 <signal handler called>
[frame] operator==: l={stack=0x0,code=0x00000000ffffffff,!special},
r={!stack,!code,!special} -> 0
[frame] get_prev_frame: enter
[frame] get_prev_frame_always_1: enter
[frame] get_prev_frame_always_1: this_frame=1
[frame] get_prev_frame_always_1: -> nullptr // UNWIND_OUTERMOST
[frame] get_prev_frame_always_1: exit
[frame] get_prev_frame: exit
[frame] get_prev_frame_always_1: enter
[frame] get_prev_frame_always_1: this_frame=1
[frame] get_prev_frame_always_1: -> nullptr // UNWIND_OUTERMOST //
cached
[frame] get_prev_frame_always_1: exit
[frame] get_prev_frame_always_1: enter
[frame] get_prev_frame_always_1: this_frame=-1
[frame] get_prev_frame_always_1: ->
{level=0,type=NORMAL_FRAME,unwinder="arm
prologue",pc=0x800609c,id={stack=0x20020000,code=0x000000000800609c,!special},func=0x800609c}
// cached
[frame] get_prev_frame_always_1: exit
The situation when stopped in an ISR is similar, just the magic value
differs e.g. 0xfffffff1
and other frames follows.
next prev parent reply other threads:[~2023-01-10 16:33 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-10-21 11:58 Tomas Vanek
2022-10-21 11:58 ` Tomas Vanek
2022-10-27 10:31 ` Luis Machado
2022-10-27 17:46 ` Tomas Vanek
2022-11-22 6:48 ` Tomas Vanek
2022-11-22 7:27 ` Luis Machado
2022-11-28 11:48 ` [PING] " Tomas Vanek
2022-12-08 1:15 ` Luis Machado
2022-12-21 8:52 ` [PING 2] " Tomas Vanek
2023-01-10 13:19 ` [PING 3] " Tomas Vanek
2023-01-10 15:31 ` Simon Marchi
2023-01-10 16:33 ` Tomas Vanek [this message]
2023-01-10 17:48 ` Simon Marchi
2023-01-10 23:22 ` Tomas Vanek
2023-01-11 1:38 ` Simon Marchi
2023-02-02 6:38 ` Tomas Vanek
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=c6e692a7-2b67-1e97-223d-31a3395d63bf@fbl.cz \
--to=vanekt@fbl.cz \
--cc=gdb-patches@sourceware.org \
--cc=simark@simark.ca \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).