This is the mail archive of the
ecos-discuss@sourceware.org
mailing list for the eCos project.
Redboot GDB Stub
- From: Narinder Dhillon <narinder_dhillon at yahoo dot ca>
- To: ecos-discuss at ecos dot sourceware dot org
- Date: Fri, 11 Mar 2011 15:38:11 -0800 (PST)
- Subject: [ECOS] Redboot GDB Stub
Redboot GDB Stub
----------------
Introduction
------------
Recently I ported eCos to Freescale MPC837X processor and could not get GDB
working. I had to
debug the GDB stub in redboot and got to know how it works. This little note is
my attempt at
explaining how the stub works, in case anyone else has to debug it.
This explanation is geared towards the Redboot GDB stub on a POwerPC CPU but is
equally applicable
to all other platforms. The GDB was used through serial port only.
How to enable GDB in Redboot
----------------------------
My Redboot's startup type is ROMRAM and the following configuration is set.
The CYGDBG_HAL_DEBUG_GDB_INCLUDE_STUBS option is enabled. It depends on the
presence of the
following lines in my platform cdl file:
implements CYGINT_HAL_DEBUG_GDB_STUBS
implements CYGINT_HAL_DEBUG_GDB_STUBS_BREAK
The CYGDBG_HAL_DEBUG_GDB_BREAK_SUPPORT option is selected to enable the Ctrl-C
option to get into
gdb stub.
The CYGDBG_HAL_DIAG_TO_DEBUG_CHAN option is selected and the mangler used is
'GDB'. This enables
GDB to use serial port for communication.
This means that your serial port has to be working before you can get GDB
working.
All the relevant files
----------------------
The generic stub implementation is located in
'hal/common/current/' 'generic_stub.h/.c', 'hal_stub.h/.c', 'thread-packets.c',
'thread-pkts.h'
and some other files in this area.
The platform specific files, for PowerPC implementation are located in
'hal/powerpc/arch/current' 'ppc_stub.h/.c', hal_arch.h'
Again, other platforms should have similar files.
How does the GDB stub kick in
-----------------------------
Once you have Redboot running on your platform, and you can see the Redboot
prompt on your serial
debug port you are ready.
Anything that is typed on Redboot prompt is processed in a while loop in
'redboot/current/src/main.c'
file. When GDB stub is included in Redboot, this loop looks for '+' and '$'
characters. When a GDB
client tries to connect to the target using the 'target remote < >' command, it
sends a GDB packet
to the target. Since GDB packet starts with '$' character, the while loop
detects it and generates
a break instruction exception. This exception causes the interrupt handler to
eventually call the
'cyg_hal_exception_handler'. This handler is defined in
'hal/powerpc/arch/current/src/hal_misc.c'
file on PowerPC platforms, it should defined similarly for other platforms.
'cyg_hal_exception_handler' is called with a pointer to the stack that has all
the registers saved
by the interrupt handler (cyg_hal_default_exception_vsr). This pointer is saved
in a pointer variable
'_hal_registers' (more on this later). Then '__handle_exception' is called. This
function is in
'hal/common/current/src/generic-stub.c', now we are in GDB.
In order to get into GDB stub, all we needed was a 'target remote' command to be
issued. This command
will now get processed.
Handling Exception
------------------
One of the first things that are done in '__handle_exception', is to save the
registers that are on the
stack (pointed to by _hal_registers) to the internal GDB array (_registers).
This is done in
'handle_exception_cleanup' coded in hal_stub.c file.
The next step is to find the what kind of signal was received. Since we just got
into this function
because of 'target remote' command, the Redboot has already indicated to the
stub that there was a
break received (either Ctrl-C or the first GDB packet in this case). Hence the
signal is SIGINT. If
we did not get here because of a break, then the signal value is determined by
calling function
'__computeSignal(__get_trap_number())'. The '__get_trap_number()' function
returns the vector number.
If the signal is a non-zero value, the exception further processes by calling
'process_exception'.
Process Exception
-----------------
In this function we sit in a loop and keep reading the packets from serial port
and processing them.
The only way we get out of this loop is by 'continuing' with the application
that we are debugging,
single stepping, detaching, killing, or file IO.
Process Packet
--------------
Every packet received in the above loop is processed by '__process_packet'
function. This is where
all the GDB commands are processed.
Let take the example of two commands that are used to run an application from
GDB.
>powerpc-eabi-gdb --symbols app.elf -b 38400 -nw
gdb>tar rem /dev/ttySI166
gdb>jump *0x100000
The fist call starts the gdb with the elf file of the application supplying the
symbols. The '-b'
option sets the baud rate and '-nw' asks for no window.
Once GDB starts, you are at 'gdb' prompt. Here, we first connect to the target
with the 'target remote'
command and the serial device port that the target debug port is connected to.
Then we tell GDB to start executing application at address 0x100000.
The 'tar rem' command gets us all the way to the 'process_exception' function
where we read the packet
containing the 'tar rem' command and then process it. The '__process_packet'
functions processes it
and sends an ack back. Then it reads the next packet. It does this a few times
to get connected to
the client program.
Then in response to 'jump' command, the client program tells the stub to set the
program counter to
0x100000. This is done in the GDB internal register array (_registers). As part
of the 'jump', the
client program tells the stub to 'continue' from 0x100000. The process packet
function at this time
does some processing and returns with -1 and causes the loop in
'process_exception' to break and
return back to '__handle_exception' function.
At this point, the handle exception function copies the GDB internal register
array (with
PC = 0x100000) and copies it to _hal_registers (which is pointing to stack,
effectively changing the
save PC value to 0x100000).
When this whole thing unwinds, we get back to the interrupt handler that was
processing the break
instruction exception. This interrupt handler then restores the saved registers
and when CPU
processes the return from interrupt command, the CPU ends up with program
counter at 0x100000.
This is how the continue commands ends up with application running from inside
GDB.
Single Step
-----------
The single step is exactly same code flow as 'continue' above, except that at
one point, the CPU is
setup to execute one instructions at a time.
This is the first cut of this tutorial, feel free to critique.
Narinder Dhillon
--
Before posting, please read the FAQ: http://ecos.sourceware.org/fom/ecos
and search the list archive: http://ecos.sourceware.org/ml/ecos-discuss