public inbox for gdb@sourceware.org
 help / color / mirror / Atom feed
* GDB call and p func() on embedded target
@ 2021-08-14 10:28 Maximilian Schneider
  2021-08-14 13:05 ` Simon Marchi
  0 siblings, 1 reply; 6+ messages in thread
From: Maximilian Schneider @ 2021-08-14 10:28 UTC (permalink / raw)
  To: gdb

Hello,

I am trying to call a function from withing a gdb debugging session on
an embedded target. However gdb never returns and when i stop it
manually the program counter is in a strange place. It appears that gdb
is not able to catch the return, and is continuing  execution from
before the core was halted... 

assume the function I want to call has prototype int32_t Init(void);

If I set the pc manually I can step throught the function until the
return without a problem.

I can even set a breakpoint on Init and then use p Init(). The
breakpoint will fire and I can step throught the code manually.

eg.

target remote localhost:3333
monitor reset halt
monitor reset init
file loader.elf
load loader.elf
set $sp=0x20010000
set $pc=&Init
b Init
p Init()
n
n
n
...

However without the breakpoint gdb never returns.
Is this a known problem? Is there a workaround?

Some background:
The purpose of this entire excercise is to arrive at a tool/set of
scripts that can be used to load and execute arbitrary code from RAM,
to fi. manipulate external memories or run tests.

Regards,
M


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: GDB call and p func() on embedded target
  2021-08-14 10:28 GDB call and p func() on embedded target Maximilian Schneider
@ 2021-08-14 13:05 ` Simon Marchi
  2021-08-14 15:54   ` Maximilian Schneider
  0 siblings, 1 reply; 6+ messages in thread
From: Simon Marchi @ 2021-08-14 13:05 UTC (permalink / raw)
  To: Maximilian Schneider, gdb

On 2021-08-14 6:28 a.m., Maximilian Schneider via Gdb wrote:
> Hello,
> 
> I am trying to call a function from withing a gdb debugging session on
> an embedded target. However gdb never returns and when i stop it
> manually the program counter is in a strange place. It appears that gdb
> is not able to catch the return, and is continuing  execution from
> before the core was halted... 
> 
> assume the function I want to call has prototype int32_t Init(void);
> 
> If I set the pc manually I can step throught the function until the
> return without a problem.
> 
> I can even set a breakpoint on Init and then use p Init(). The
> breakpoint will fire and I can step throught the code manually.
> 
> eg.
> 
> target remote localhost:3333
> monitor reset halt
> monitor reset init
> file loader.elf
> load loader.elf
> set $sp=0x20010000
> set $pc=&Init
> b Init
> p Init()
> n
> n
> n
> ...
> 
> However without the breakpoint gdb never returns.
> Is this a known problem? Is there a workaround?
> 
> Some background:
> The purpose of this entire excercise is to arrive at a tool/set of
> scripts that can be used to load and execute arbitrary code from RAM,
> to fi. manipulate external memories or run tests.
> 
> Regards,
> M

Hi Maximilian,

I don't think there would be a way to solve this other than debugging
GDB itself, stepping in the call_function_by_hand_dummy function to
understand what it does, how it sets up the dummy stack frame, what
technique it uses to put the breakpoint that should lead to the function
call being completed, etc..  And also running with "set debug infrun 1"
and possibly stepping through handle_inferior_event, to see why GDB
doesn't realize the function call isn't over.

The call_function_by_hand_dummy really lacks debug prints, making it
hard to debug when it goes wrong.  I was debugging another function call
issue the other day, and thought to myself that it would be a good time
to add some.

Also, inferior function call has a lot of arch-specific components to
it, so if you could mention the arch you are working on, it could help
narrow things down.

Simon


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: GDB call and p func() on embedded target
  2021-08-14 13:05 ` Simon Marchi
@ 2021-08-14 15:54   ` Maximilian Schneider
  2021-08-16  1:14     ` Simon Marchi
  0 siblings, 1 reply; 6+ messages in thread
From: Maximilian Schneider @ 2021-08-14 15:54 UTC (permalink / raw)
  To: Simon Marchi, gdb

Hello,

Some more information:

I'm working on an stm32l475.

using openocd like so:
openocd -v
Open On-Chip Debugger 0.10.0+dev-g436782b (2019-03-05-19:20)

openocd -f interface/stlink-v2-1.cfg -f target/stm32l4x.cfg -d3
And the executable I debug is compiled like this:
arm-none-eabi-gcc -Wall -Werror -Tscript.ld -Wl,-n,-N -nostartfiles
-ggdb -g -nostdlib -FPIC -FPIE -mcpu=cortex-m4 $(INCLUDES) $(DEFINES)
src/loader.c -o loader.elf

I use a linker script to put the code section into RAM.

I should als mention the gdb version:
arm-none-eabi-gdb -v
GNU gdb (7.12-6+9+b2) 7.12.0.20161007-git

I will try compiling the latest gdb from source, and debug it a little
later. In the mean time I can demonstrate gdb is creating stack frames.
Maybe you can spot something wrong with them?

(gdb) set debug infrun 1
(gdb) p Init()
infrun: clear_proceed_status_thread (Remote target)
infrun: proceed (addr=0x20000aac, signal=GDB_SIGNAL_0)
infrun: resume (step=0, signal=GDB_SIGNAL_0), trap_expected=0, current thread [Remote target] at 0x20000aac
infrun: infrun_async(1)
infrun: prepare_to_wait
infrun: target_wait (-1.0.0, status) =
infrun:   -1.0.0 [Thread 0],
infrun:   status->kind = ignore
infrun: TARGET_WAITKIND_IGNORE
infrun: prepare_to_wait

* just sits here *

I don't see any comments about creating a stack frame.
But gdb definately creates one.
I can inspect the stack frame after the call is made

eg:
I place a breakpoint in Init. and use p Init() twice.
(gdb) p Init()
...
(gdb) p Init()
..
(gdb) info frame
Stack level 0, frame at 0x2000ffe0:
 pc = 0x20000ab2 in Init (src/loader.c:718); saved pc = 0x0
 called by frame at 0x2000ffe0
 source language c.
 Arglist at 0x2000ffd0, args: 
 Locals at 0x2000ffd0, Previous frame's sp is 0x2000ffe0
 Saved registers:
  r7 at 0x2000ffd8, lr at 0x2000ffdc
(gdb) info frame 1
Stack frame at 0x2000ffe0:
 pc = 0x0; saved pc = 0x20000ab2
 called by frame at 0x2000fff8, caller of frame at 0x2000ffe0
 Arglist at unknown address.
 Locals at unknown address, Previous frame's sp is 0x2000ffe8
(gdb) info symbol 0x20000ab2
Init + 6 in section PrgCode

This is creating two stack frames as expected, and I can step the inner
function.
When I step through the return there is a pop instruction that restores
the PC from the stack frame and execution resumes in the calee.

What is pc= vs saved pc=? Is it relevant that pc=0x0?

Regards,
M.

On Sat, 2021-08-14 at 09:05 -0400, Simon Marchi wrote:
> On 2021-08-14 6:28 a.m., Maximilian Schneider via Gdb wrote:
> > Hello,
> > 
> > I am trying to call a function from withing a gdb debugging session
> > on
> > an embedded target. However gdb never returns and when i stop it
> > manually the program counter is in a strange place. It appears that
> > gdb
> > is not able to catch the return, and is continuing  execution from
> > before the core was halted... 
> > 
> > assume the function I want to call has prototype int32_t
> > Init(void);
> > 
> > If I set the pc manually I can step throught the function until the
> > return without a problem.
> > 
> > I can even set a breakpoint on Init and then use p Init(). The
> > breakpoint will fire and I can step throught the code manually.
> > 
> > eg.
> > 
> > target remote localhost:3333
> > monitor reset halt
> > monitor reset init
> > file loader.elf
> > load loader.elf
> > set $sp=0x20010000
> > set $pc=&Init
> > b Init
> > p Init()
> > n
> > n
> > n
> > ...
> > 
> > However without the breakpoint gdb never returns.
> > Is this a known problem? Is there a workaround?
> > 
> > Some background:
> > The purpose of this entire excercise is to arrive at a tool/set of
> > scripts that can be used to load and execute arbitrary code from
> > RAM,
> > to fi. manipulate external memories or run tests.
> > 
> > Regards,
> > M
> 
> Hi Maximilian,
> 
> I don't think there would be a way to solve this other than debugging
> GDB itself, stepping in the call_function_by_hand_dummy function to
> understand what it does, how it sets up the dummy stack frame, what
> technique it uses to put the breakpoint that should lead to the
> function
> call being completed, etc..  And also running with "set debug infrun
> 1"
> and possibly stepping through handle_inferior_event, to see why GDB
> doesn't realize the function call isn't over.
> 
> The call_function_by_hand_dummy really lacks debug prints, making it
> hard to debug when it goes wrong.  I was debugging another function
> call
> issue the other day, and thought to myself that it would be a good
> time
> to add some.
> 
> Also, inferior function call has a lot of arch-specific components to
> it, so if you could mention the arch you are working on, it could
> help
> narrow things down.
> 
> Simon
> 


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: GDB call and p func() on embedded target
  2021-08-14 15:54   ` Maximilian Schneider
@ 2021-08-16  1:14     ` Simon Marchi
  2021-08-18 20:31       ` Maximilian Schneider
  0 siblings, 1 reply; 6+ messages in thread
From: Simon Marchi @ 2021-08-16  1:14 UTC (permalink / raw)
  To: Maximilian Schneider, gdb



On 2021-08-14 11:54 a.m., Maximilian Schneider wrote:
> Hello,
> 
> Some more information:
> 
> I'm working on an stm32l475.
> 
> using openocd like so:
> openocd -v
> Open On-Chip Debugger 0.10.0+dev-g436782b (2019-03-05-19:20)
> 
> openocd -f interface/stlink-v2-1.cfg -f target/stm32l4x.cfg -d3
> And the executable I debug is compiled like this:
> arm-none-eabi-gcc -Wall -Werror -Tscript.ld -Wl,-n,-N -nostartfiles
> -ggdb -g -nostdlib -FPIC -FPIE -mcpu=cortex-m4 $(INCLUDES) $(DEFINES)
> src/loader.c -o loader.elf
> 
> I use a linker script to put the code section into RAM.
> 
> I should als mention the gdb version:
> arm-none-eabi-gdb -v
> GNU gdb (7.12-6+9+b2) 7.12.0.20161007-git
> 
> I will try compiling the latest gdb from source, and debug it a little
> later. In the mean time I can demonstrate gdb is creating stack frames.
> Maybe you can spot something wrong with them?
> 
> (gdb) set debug infrun 1
> (gdb) p Init()
> infrun: clear_proceed_status_thread (Remote target)
> infrun: proceed (addr=0x20000aac, signal=GDB_SIGNAL_0)
> infrun: resume (step=0, signal=GDB_SIGNAL_0), trap_expected=0, current thread [Remote target] at 0x20000aac
> infrun: infrun_async(1)
> infrun: prepare_to_wait
> infrun: target_wait (-1.0.0, status) =
> infrun:   -1.0.0 [Thread 0],
> infrun:   status->kind = ignore
> infrun: TARGET_WAITKIND_IGNORE
> infrun: prepare_to_wait
> 
> * just sits here *
> 
> I don't see any comments about creating a stack frame.
> But gdb definately creates one.
> I can inspect the stack frame after the call is made
> 
> eg:
> I place a breakpoint in Init. and use p Init() twice.
> (gdb) p Init()
> ...
> (gdb) p Init()
> ..
> (gdb) info frame
> Stack level 0, frame at 0x2000ffe0:
>  pc = 0x20000ab2 in Init (src/loader.c:718); saved pc = 0x0
>  called by frame at 0x2000ffe0
>  source language c.
>  Arglist at 0x2000ffd0, args: 
>  Locals at 0x2000ffd0, Previous frame's sp is 0x2000ffe0
>  Saved registers:
>   r7 at 0x2000ffd8, lr at 0x2000ffdc
> (gdb) info frame 1
> Stack frame at 0x2000ffe0:
>  pc = 0x0; saved pc = 0x20000ab2
>  called by frame at 0x2000fff8, caller of frame at 0x2000ffe0
>  Arglist at unknown address.
>  Locals at unknown address, Previous frame's sp is 0x2000ffe8
> (gdb) info symbol 0x20000ab2
> Init + 6 in section PrgCode
> 
> This is creating two stack frames as expected, and I can step the inner
> function.
> When I step through the return there is a pop instruction that restores
> the PC from the stack frame and execution resumes in the calee.
> 
> What is pc= vs saved pc=? Is it relevant that pc=0x0?

The execution out of the manual function call isn't expected to end
"just like that", with a return.  Normally, when the called function
returns, it should return to some code (the dummy stack frame) that
generates some exception, so that GDB gets back control.  How this is
done varies from arch to arch.  This is where it happens:

  https://gitlab.com/gnutools/binutils-gdb/-/blob/master/gdb/infcall.c#L956-1012

For ARM, it seems like gdbarch_call_dummy_location uses the default
AT_ENTRY_POINT, meaning that GDB puts a breakpoint at the address
returned by entry_point_address(), and sets up the stack frame created
to call Init to return there.  Maybe with your executable the entry
point returns 0, and that's why you see frame 1 having pc == 0?

Simon

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: GDB call and p func() on embedded target
  2021-08-16  1:14     ` Simon Marchi
@ 2021-08-18 20:31       ` Maximilian Schneider
  2021-08-18 20:49         ` Simon Marchi
  0 siblings, 1 reply; 6+ messages in thread
From: Maximilian Schneider @ 2021-08-18 20:31 UTC (permalink / raw)
  To: Simon Marchi, gdb

On Sun, 2021-08-15 at 21:14 -0400, Simon Marchi wrote:
> On 2021-08-14 11:54 a.m., Maximilian Schneider wrote:
> > Hello,
> > 
> > Some more information:
> > 
> > I'm working on an stm32l475.
> > 
> > using openocd like so:
> > openocd -v
> > Open On-Chip Debugger 0.10.0+dev-g436782b (2019-03-05-19:20)
> > 
> > openocd -f interface/stlink-v2-1.cfg -f target/stm32l4x.cfg -d3
> > And the executable I debug is compiled like this:
> > arm-none-eabi-gcc -Wall -Werror -Tscript.ld -Wl,-n,-N -nostartfiles
> > -ggdb -g -nostdlib -FPIC -FPIE -mcpu=cortex-m4 $(INCLUDES)
> > $(DEFINES)
> > src/loader.c -o loader.elf
> > 
> > I use a linker script to put the code section into RAM.
> > 
> > I should als mention the gdb version:
> > arm-none-eabi-gdb -v
> > GNU gdb (7.12-6+9+b2) 7.12.0.20161007-git
> > 
> > I will try compiling the latest gdb from source, and debug it a
> > little
> > later. In the mean time I can demonstrate gdb is creating stack
> > frames.
> > Maybe you can spot something wrong with them?
> > 
> > (gdb) set debug infrun 1
> > (gdb) p Init()
> > infrun: clear_proceed_status_thread (Remote target)
> > infrun: proceed (addr=0x20000aac, signal=GDB_SIGNAL_0)
> > infrun: resume (step=0, signal=GDB_SIGNAL_0), trap_expected=0,
> > current thread [Remote target] at 0x20000aac
> > infrun: infrun_async(1)
> > infrun: prepare_to_wait
> > infrun: target_wait (-1.0.0, status) =
> > infrun:   -1.0.0 [Thread 0],
> > infrun:   status->kind = ignore
> > infrun: TARGET_WAITKIND_IGNORE
> > infrun: prepare_to_wait
> > 
> > * just sits here *
> > 
> > I don't see any comments about creating a stack frame.
> > But gdb definately creates one.
> > I can inspect the stack frame after the call is made
> > 
> > eg:
> > I place a breakpoint in Init. and use p Init() twice.
> > (gdb) p Init()
> > ...
> > (gdb) p Init()
> > ..
> > (gdb) info frame
> > Stack level 0, frame at 0x2000ffe0:
> >  pc = 0x20000ab2 in Init (src/loader.c:718); saved pc = 0x0
> >  called by frame at 0x2000ffe0
> >  source language c.
> >  Arglist at 0x2000ffd0, args: 
> >  Locals at 0x2000ffd0, Previous frame's sp is 0x2000ffe0
> >  Saved registers:
> >   r7 at 0x2000ffd8, lr at 0x2000ffdc
> > (gdb) info frame 1
> > Stack frame at 0x2000ffe0:
> >  pc = 0x0; saved pc = 0x20000ab2
> >  called by frame at 0x2000fff8, caller of frame at 0x2000ffe0
> >  Arglist at unknown address.
> >  Locals at unknown address, Previous frame's sp is 0x2000ffe8
> > (gdb) info symbol 0x20000ab2
> > Init + 6 in section PrgCode
> > 
> > This is creating two stack frames as expected, and I can step the
> > inner
> > function.
> > When I step through the return there is a pop instruction that
> > restores
> > the PC from the stack frame and execution resumes in the calee.
> > 
> > What is pc= vs saved pc=? Is it relevant that pc=0x0?
> 
> The execution out of the manual function call isn't expected to end
> "just like that", with a return.  Normally, when the called function
> returns, it should return to some code (the dummy stack frame) that
> generates some exception, so that GDB gets back control.  How this is
> done varies from arch to arch.  This is where it happens:
> 
>   
> https://gitlab.com/gnutools/binutils-gdb/-/blob/master/gdb/infcall.c#L956-1012
> 
> For ARM, it seems like gdbarch_call_dummy_location uses the default
> AT_ENTRY_POINT, meaning that GDB puts a breakpoint at the address
> returned by entry_point_address(), and sets up the stack frame
> created
> to call Init to return there.  Maybe with your executable the entry
> point returns 0, and that's why you see frame 1 having pc == 0?
> 
> Simon

Thank you for the great information!

Turns out it was the start address. Since gdb is setting up a frame to
call my function and then return to the start address; it is no wonder
that it does not work. 0 is neither ram not flash so sw breakpoints
will not work there. HW breakpoints probably won't work there either.

I added -e 0x2000000 to the compilation to set an explicit entry point
and huzza it works :)

If gdb is already putting code on the stack to call my function why not
implement a sw breakpoint there? Why return to start?

Why does gdb care at all what the start address is? Since this is an
external loader that lives in ram and has no main() i don't see what
the point of the entry point is.

M.


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: GDB call and p func() on embedded target
  2021-08-18 20:31       ` Maximilian Schneider
@ 2021-08-18 20:49         ` Simon Marchi
  0 siblings, 0 replies; 6+ messages in thread
From: Simon Marchi @ 2021-08-18 20:49 UTC (permalink / raw)
  To: Maximilian Schneider, gdb

On 2021-08-18 4:31 p.m., Maximilian Schneider wrote:
> Thank you for the great information!
> 
> Turns out it was the start address. Since gdb is setting up a frame to
> call my function and then return to the start address; it is no wonder
> that it does not work. 0 is neither ram not flash so sw breakpoints
> will not work there. HW breakpoints probably won't work there either.
> 
> I added -e 0x2000000 to the compilation to set an explicit entry point
> and huzza it works :)

Ok, good news!

> If gdb is already putting code on the stack to call my function why not
> implement a sw breakpoint there? Why return to start?
> 
> Why does gdb care at all what the start address is? Since this is an
> external loader that lives in ram and has no main() i don't see what
> the point of the entry point is.

GDB just needs a place it can put a breakpoint and redirect execution
to, to get back control when the inferior function call is done.  It
just happens that the entry point usually works fine for that, at least
when you are talking about a program that runs on an OS like Linux.  I
guess it just doesn't work in all cases, such as yours.

As we saw, other architectures do use the stack for this, they have
gdbarch_call_dummy_location return ON_STACK and implement
gdbarch_push_dummy_code.  I suppose you would have to make the ARM
architecture do that to make it work properly in your case.

Simon

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2021-08-18 20:49 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-08-14 10:28 GDB call and p func() on embedded target Maximilian Schneider
2021-08-14 13:05 ` Simon Marchi
2021-08-14 15:54   ` Maximilian Schneider
2021-08-16  1:14     ` Simon Marchi
2021-08-18 20:31       ` Maximilian Schneider
2021-08-18 20:49         ` Simon Marchi

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).