GDB Tutorial - TeamAquasHideout/Team-Aquas-Asset-Repo GitHub Wiki
Using GDB with pokeemerald
The GNU Debugger (GDB) is a very powerful tool for inspecting your code at runtime to aid in tracking down bugs. In this tutorial I will briefly cover the process of setting up gdb for use with mGBA and pokeemerald.
[!NOTE] This tutorial covers WSL and Linux. macOS is currently not covered.
Getting GDB
Getting gdb set up is simple. Just install the gdb package using your distribution's package manager. On some distros (debian derivates) the gdb package that targets multiple architectures is called gdb-multiarch and provided separately from the regular gdb package.
Activating mGBA's gdb server
In order to interface with gdb, gba provides a remote debugging server. This can be activated from Tools -> Start GDB Server in mGBA once you have a rom open. By default the gdb server runs on localhost at port 2345. You may also use the -g flag when running mGBA from the terminal to start the debug server on launch.
Building pokeemerald with debug information
In order to use gdb to it's fullest potential we need to make sure that the rom is built with debug information embedded. To do so use make debug. You may also do DEBUG=1 make (This can be useful for debugging firered or leafgreen). This will keep necessary debug information such as symbol names and line numbers in the elf. Once you've done this you're ready to start debugging.
[!WARNING] The
.gbafile is stripped of all the additional data embedded into the.elfso you will need to use the.elffor debugging. mGBA loads this just fine.
Using GDB to debug
-
First start up mgba and load the
.elffile built with debug information. -
Then start the gdb server from the Tools menu.
-
On your terminal start
gdb. You'll be greeted with thegdbcli interface. -
We'll need to load the
.elffile intogdbso that it has access to the debug information it needs. Run filepokeemerald.elfinside the gdb interface. You may also invoke gdb withgdb -se pokeemerald.elfto do this on launch. -
Now we need to run the following commands to set the target architecture to arm and to connect to our server.
set architecture armtarget remote localhost:2345
Port 2345 is the default for mGBA's gdb server. If you've changed that then specify the correct port here.
With that your gdb instance should be connected to mGBA and you can start using it to debug.
GDB Basics
[!WARNING] This section is still a WIP
The GDB manual page covers the basic commands. Much of the content below has been adapted from this tutorial.
Breakpoints
Breakpoints represent places in your program where the execution should be stopped. They are added using the break command. Here are the most common usages:
-
break function- Set a breakpoint at function entry. When using source languages that permit overloading of symbols, such as C++, function may refer to more than one possible place to break. -
break linenum- Set a breakpoint at line linenum in the current source file. The current source file is the last file whose source text was printed. The breakpoint will stop your program just before it executes any of the code on that line. -
break filename:linenum- Set a breakpoint at line linenum in source file filename. -
break filename:func- Set a breakpoint at entry of the function func found in file filename. Specifying a file name as well as a function name is superfluous except when multiple files contain similarly named functions. -
break *addr- Set a breakpoint at address addr. You can use this to set breakpoints in parts of your program which do not have debugging information or source files.
You can see an overview of the current breakpoints using the info break command.
Deleting breakpoints
In order to remove breakpoints, you can use the clear or the delete (d) command. With the clear command you can delete breakpoints according to where they are in your program. With the delete command you can delete individual breakpoints by specifying their breakpoint numbers.
gdb> delete 2
gdb> clear src/main.c:6
Stepping
There might be situations when you only want to execute one line of source code, or one machine instruction from your program. This action is called step and can be categorized as follows:
-
step or s (step into)- Continue running your program until control reaches a different source line, then stop it and return control to GDB. If the line you are stepping over represents a function call, this command will step inside it. -
next or n (step over)- Continue to the next source line in the current stack frame. This is similar to step, but function calls that appear within the line of code are executed without stopping.
There are also equivalent functions for the underlying assembly instructions: stepi and nexti. Those are useful when the binary uses various compiler optimizations and the source lines debug metadata are unreliable.
If you stepped into a function and you want to continue the execution until the function returns, you can use the finish or f (step out) command.
Stack Trace
A backtrace is a summary of how your program got where it is. It shows one line per frame, for many frames, starting with the currently executing frame (frame zero), followed by its caller (frame one), and on up the stack.
backtrace or bt - Print a backtrace of the entire stack: one line per frame for all frames in the stack.
GDB Editor Integration
While command line gdb is powerful, it is not nearly as convenient as intergrating it into your editor. Thankfully, Microsoft (stopped clock yada yada) has given us the Debug Adapter Protocol or DAP which makes this very convenient. For neovim this can be done with the nvim-dap and nvim-dap-ui plugins. This is how I have it set up.
{
"mfussenegger/nvim-dap",
config = function()
local dap = require("dap")
dap.adapters.gba = {
type = "executable",
command = "gdb",
args = {
"-ex",
"set architecture arm",
"-i",
"dap",
},
}
local mgba = {
name = "mGBA",
type = "gba",
request = "attach",
program = function()
local elf_file = vim.fn.input("ELF file: ", vim.fn.getcwd() .. "/", "file")
return elf_file
end,
cwd = vim.fn.getcwd(),
target = "localhost:2345",
}
table.insert(dap.configurations.c, mgba)
end,
}
VS Code instructions will soon follow. This blog post might be of assistance in the meantime (YMMV).