[ECOS] Redboot GDB Stub

Narinder Dhillon narinder_dhillon@yahoo.ca
Wed Mar 16 14:22:00 GMT 2011

Redboot GDB Stub


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

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 

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', 
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 
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 
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

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 

>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 

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

More information about the Ecos-discuss mailing list