Skip to content

Allow using gdb to connect to an N64 flash cart to debug C code. Allowing for inspecting values, pausing at breakpoints and stepping through code.

License

Notifications You must be signed in to change notification settings

lambertjamesd/libultragdb

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

45 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

libultragdb

Allows for debugging over a flashcart serial port using GDB. This library is designed to use threads and at the moment only supports libultra. It uses the same protocol as UNFLoader to communicate.

Getting Started

You need to include the debugger folder in your project and at the start of your program call.

enum GDBError gdbInitDebugger(OSPiHandle* handler, OSMesgQueue* dmaMessageQ, OSThread** forThreads, u32 forThreadsLen);

handler should be the return value for osCartRomInit. dmaMessageQ is the message queue used to coordinate DMA access. forThreads is an array of threads the debugger should connect to and forThreadsLen is the length of forThreads

See the example included in this repo for a concrete example of usage.

Starting proxy.js

Proxy.js translates commands sent to and from GDB to a format the serial interface can process. You will need to use it to connect the debugger. To run proxy.js you will need to install node.

To run it use

node proxy/proxy.js /dev/ttyUSB0 8080 -k

Where /dev/ttyUSB0 is the serial port the flash cart is connected to and 8080 is the port the proxy listens to for GDB connections. the -k flag is used to indicate that the proxy should stay open even after GDB disconnects. This allows you to reuse the same proxy process instead of having to restart it after each run. You can also optionally add a -v flag and proxy will print verbose information to diagnose connection problems.

Connecting GDB

If found I needed to use gdb-multiarch to get remote debugging to work.

sudo apt-get install gdb-multiarch

Before starting GDB you should first have the ROM you want to debug running on the target system and paused at gdbInitDebugger waiting for the debugger to connect. You also need the proxy running. Once you have done that run gdb

gdb-multiarch -q debugger.out

Where debugger.out is the intermediate elf file generated by mild or spicy

You should then be greeted by the GDB prompt

Reading symbols from debugger.out...
(gdb) 

Next you need to connect to proxy. You do that by specifying a remote target to GDB.

(gdb) target remote localhost:8080

If things are working correctly you should see something like this

Remote debugging using localhost:8080
0x800083cc in osStopThread ()
(gdb) 

At this point you can continue the program and use GDB as normal. Here are a few things to try.

(gdb) c
Continuing.

Program received signal SIGTRAP, Trace/breakpoint trap.
gdbInitDebugger (handler=0x80024178 <__CartRomHandle>, dmaMessageQ=0x8001c170 <n_dmaMessageQ>, forThreads=0x8002037c <mainThreadStack+8164>, forThreadsLen=1) at debugger/debugger.c:893
893         return GDBErrorNone;
(gdb) c
Continuing.

Breakpoint 1, mainproc (arg=0x0) at example/main.c:172
172           healthcheck = 0;
(gdb) print frame
$1 = 0
(gdb) del 1
(gdb) c
Continuing.
^C
Program received signal SIGINT, Interrupt.
0x80005568 in putchr (color=65535, curs_x=61, curs_y=24, c=32 ' ') at example/graph.c:99
99                    *p = BGCOLOR;
(gdb) 

VSCode Plugins

I recommend this plugin for debugging

GDB Debugger - Beyond

Native Debug

These are the targets I have in launch.json to enable these plugins.

Where Run EV delay is a task that uploads the rom build/debugger.n64 to the flash cart and waits a short delay for the game to load be ready for the debugger to connect.

        {
            "type": "by-gdb",
            "request": "launch",
            "name": "Debug with EV",
            "program": "${workspaceFolder}/build/debugger.elf",
            "cwd": "${workspaceRoot}",
            "debuggerPath": "gdb-multiarch",
            "remote": {
                "enabled": true,
                "address": ":8080",
                "mode": "remote",
                "execfile": "${workspaceFolder}/build/debugger.elf"
            },
            "commandsBeforeExec": [
                "set arch mips:4300",
            ],
            "preLaunchTask": "Run EV delay",
        },

Potential Questions

No debugging symbols found

If gdb gives you this error

(No debugging symbols found in debugger.out)

That means you either didn't compile debugging symbols in the compile step or they were discarded when linking. Be sure that you include the -g flag when running mips64-elf-cc in your build process. As for the linking stage I am working on my own fork of spicy that doesn't discard the debugging symbols in the link step. As far as I can tell the symbols are discarded in the final objcpy step anyway so having the included in the intermediate debugger.out file is fine.

Why stop the main thread twice in gdbInitDebugger

Here is the code in question

    if (primaryThread != NULL) {
        osStopThread(primaryThread);
        gdbBreak();
    }

For some reason the Native Debug vscode plugin immediatly continues after connecting but thinks that the program is still paused. It is only upon reaching the second gdbBreak breakpoint that it will pause and the debugger will work properly. You also may be wondering why even have the debugger pause at all and let it continue right after connecting. Well for some reason both vs code debugger plugins wont let me interrupt execution if I don't first stop at that breakpoint.

Cen64

This debugger does work with a special fork of cen64 but a much better solution is to use cen64's built in gdb debugger. More details can be found here Debugging with GDB

About

Allow using gdb to connect to an N64 flash cart to debug C code. Allowing for inspecting values, pausing at breakpoints and stepping through code.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published