Welcome to the home of Ben

Navigation
 
 
 
 
 
 
 
Information
 
 
 
 

DS GDB Debugger Stub

This page contains a GDB debugger stub library which allows you to debug your DS homebrew using a ARM targeted version of GDB.

The stub is in the early stages of development but you can do useful things with it so it has been made available.

You can discuss the stub over on the gbadev.org forums.

The Tarballs

The top one is the most recent.

What has changed?

Release 20061010

  • Change to communications interface to increase TCP performance (requires dswifi hack).

Release 20060831

  • Added support for G command (setting register values).
  • All PC affecting Thumb instructions can be stepped.
  • ARM LDR and LDM instructions affecting the PC can be stepped.
  • Experimental ARM9 monitor .nds build added (see below).
  • Some corrections.

Release 20060815

  • Partial stepping - non program counter affecting instructions can be stepped. Thumb branch and pop instructions can be stepped. ARM branch instructions can be stepped.
  • Corrected r15 value given to GDB - GDB expects the r15 value to be the address of the current instruction not the value as if read from the register.
  • Avoids some data aborts when reading/writing memory via GDB - only allows access to addresses above 0x02000000.
  • Change to directory structure - pre-built libraries and headers are available in the libs and include directories. Debug comms source moved into subdirectories.
  • Some corrections.

ToDo List

  • ARM7 support.
  • Be able to break a running target (Ctrl-C command).
  • Continue from a different address.
  • Full stepping
  • Handle breakpoints on the DS. Not sure if this will happen as GDB can step over breakpoints with stepping implemented.
  • Read and write memory using the requested word size, i.e. 8bit, 16bit, 32bit. Would be useful for reading and setting registers.
  • Detect, as different, a data and prefetch abort. If a data abort occurs the DS app is very likely to die as it is assumed to be a pretch abort and will use the wrong return address.
  • Wait for GDB to reconnect if the TCP socket disconnects.

What Is Supported

Partial stepping (certain pc affecting instructions cannot be stepped), continue, memory read and write and reading the register contents are supported at the moment. Breakpointing is all handled by GDB replacing instructions in the code with undefined instructions (arm code) or breakpoint instruction (thumb code) and therefore causing an exception when these are executed. The stub does not maintain the breakpoints at all.

If you try to use unsupported features then the stub will break.

With stepping partially supported, there is no need to clear a breakpoint before continuing after the breakpoint has been hit.

Stub to PC communications

The communications and the debug stub logic are seperate (build into seperate libraries). The communications interface is given to the debug stub at runtime. Therefore you can use whatever mechanism you want for PC to stub comms.

Currently wireless TCP network comms (using dswifi library - tested with version 0.3a) and my SPI/UART bridge serial port comms have been implemented. The TCP network comms use the Nintendo WFC settings for connecting to the AP and get the IP address.

Implementing a Communications Mechanism

If you want to implement your own comms mechanism have a look at the debug_tcp.c and debug_serial.c files to see two seperate examples.

PC Side Setup

You will need a version of GDB that understands an ARM target. Windows users can use Insight/GDB available as part of devKitPro. For Linux you can get a pre compiled GDB for ARM targets from www.codesourcery.com (you will be wanting the eabi version).

For other platforms you may be able to build GDB from the source. Read through the READMEs in the source tree for help with building. The target you need to set is arm-none-eabi (./configure --target=arm-none-eabi). This has worked for me (under Linux) using version 6.4 of GDB.

For other platforms and Linux, you can get a visual interface by compiling Insight (which includes GDB). Set the target in the same way as described for compiling GDB.

Debugging the Demo Application using TCP

Put debug_tcp.nds on your DS by some means. This will run and connect to the AP and get an IP and the like. At this point the DS will sit waiting for somebody to connect to it (tcp port 30000 in the demo).

GDB will needs the corresponding .elf file to correctly inteprete what the DS stub is telling it which is debug_tcp.arm9.elf.

GDB must be told how to connect to the DS. If using the command prompt, to connect over TCP requires a command in the following form:

target remote ip_addr:port_number

Your DS will have displayed its IP address if the wifi side connected correctly. For my DS and using the demo app I type the following:

target remote 192.168.12.2:30000

If succesful the DS stub will now be connected to GDB.

The demo app immediately causes an exception (calling debugHalt()) jumping into the debug stub. This allows the DS stub and GDB to pass messages and get in sync (without this the GDB could timeout waiting for a response from the stub).

You can now do some debugging, remembering the restrictions on what is supported. If the connection is lost for some reason you will have to restart the DS.

Using the Debug Stub with Your Applications

This guide assumes you are using TCP comms.

The Debug Stub Libraries and Header Files

Inside the libs directory you will find a number of libraries.

  • libdebugstub9.a - the gdb debugger stub
  • libdebugtcp9.a - the dswifi TCP network comms code
  • libdebugserial9.a - spi/uart bridge comms code
  • libserial9.a - spi/uart bridge driver code

Copy the first two to somewhere where the linker will search for libraries (your libnds/lib directory will do). Inside the include directory you will find some header files.

  • debug_stub.h - the application facing debug stub function interface.
  • debug_tcp.h - the debug stub TCP comms interface.
  • debug_serial.h - the debug stub spi/uart bridge comms interface.
  • spi_uart_bridge.h - spi/uart bridge driver interface.

Copy the first two to somewhere the compile will search for header files (your libnds/include directory will do).

Your Application

You will need to enable wifi in your ARM7 code (an example is in the arm7 directory). In your ARM9 code include the stub and comms header:

#include <debug_stub.h>
#include <debug_tcp.h>

When using TCP comms you must call irqInit() before initialising the debug stub.

Create and initialise a struct tcp_debug_comms_init_data giving the port number to use to listen for GDB connections. Then call the init_debug function passing it a pointer to the TCP comm interface structure (tcpCommsIf_debug - defined in debug_tcp.h) and a pointer to the struct tcp_debug_comms_init_data structure you created earlier. Here is an example using port 30000:

{
  struct tcp_debug_comms_init_data init_data = {
    .port = 30000
  };
  if ( !init_debug( &tcpCommsIf_debug, &init_data)) {
    iprintf("Failed to initialise the debugger stub - cannot continue\n");
    while(1);
  }
}

Now call debugHalt() to cause your program to jump into the debug stub and allow it to sync up with the GDB debugger.

To debug your application follow the instructions given for the demo app in the first post.

ARM9 Monitor

Source in the monitor and build_things directories.

The ARM9 monitor (added version 20060831) allows an ARM9 application to be debugged by downloading the code and data from an ELF file. The application does not have to be debugger aware, only needing to be built against a special version of libnds and the ARM startup code. The monitor lives in the first 256K of the DS main memory.

Application Restrictions

The application being debugged cannot install an exception handler or using the ITCM memory area.

Any functionality required for debug communications mechanism cannot be used by the appliation. For example, if using TCP for debugging the application cannot use any wireless functionality.

The main memory used by the monitor is not available to the appliation. This restriction is defined in the linker file build_things/ds_debug_arm9.ld.

Special libnds build

libs/libdebugnds9.a

The ARM9 interrupt handler in libnds installs itself into ITCM memory. The special build of libnds simply places the interrupt handler into main memory.

ARM9 startup code

build_things/ds_debug_arm9_crt0.asm

The ARM9 startup code is the same as the normal code but with the protection unit setup, stack pointers setup and copying to ITCM and DTCM memory removed.

The build_things/ds_debug_arm9.specs file directs the application to use the startup code.

Debugging the Test Application

An example application can be found in the monitor directory. The source for the application is main.c and some_thumb_code.c and the makefile is Makefile.app. Note that the application does not get built to a .nds file but only to the .elf stage (test_app.elf)

In order to debug the application, run the monitor on the target DS (monitor_tcp.nds in most cases). This will leave the DS with two white screens and, if the wifi connects correctly, the power LED blinking. At this point the monitor is ready for GDB to connect.

Startup GDB/Insight with the test_app.elf file and connect to the target (see above). Download the application to the target using the load command or through the Insight menu. GDB sets the monitor to execute the startup code of the downloaded application.

The application can now be debugged as normal. NOTE: since there is no way to break a running target make sure to set some breakpoints before continuing.

dswifi Library Hack

This hack is against the dswifi library CVS source dated 2006-10-10.

The dswifi library TCP implementation, like most others, buffers data before sending out a TCP packet in order to reduce the number of packets sent. The buffered data is only sent once a threshold amount of data is ready or the buffered data have been waiting for a period of time.

Unfortunately this causes the debug stub TCP communication to perform badly as the stub and GDB often communicate using small packets. Therefore the small packets get buffered and have to wait for the timeout before they are sent.

Berkley socket implementations usually have a socket option (TCP_NODELAY) to cause the TCP packet to be sent immediately when the send() function is called. The dswifi library has not implemented this option yet. To hardcode this no delay behaviour change the sgIP_TCP_Send in the sgIP_TCP.c file as described below.

Find the following if statement (about line 862).

if(j>SGIP_TCP_TRANSMIT_IMMTHRESH && rec->tcpstate==SGIP_TCP_STATE_ESTABLISHED) {

Remove the threshold comparison by changing to the following.

if( rec->tcpstate==SGIP_TCP_STATE_ESTABLISHED) {