Changes
This is the fifth and final (Update: see Part 6!)
version of a series of posts working with
GDB to integrate Qemu into the start
and run
commands. While GDB
does support remote debug restarting with set remote exec-file filename
,
this does not work for Qemu. (GDB remote documentation).
Part 4
presented a fully functioning set of start
and run
commands. To
improve upon it, this post removes all use of temporary files, adds
helpful error messages, improves the clarity of the ~/.gdbinit
file
(by defining a qemu
command that hooks into start
and run
instead
of redefining one of the latter two), and finally by improving Emacs
integration to open the source window immediatley on start
.
Version 5
- Restarting
- Emacs
- Error messages
- Normal x86-64 debugging
### --------------------------------------------------------------------
### .gdbinit
### Author: William Ughetta
### Overloads `start` and `run` commands to debug aarch64 binaries with
### Qemu user-space emulation on port 1234.
### --------------------------------------------------------------------
define hook-start
set-aarch64
if ($aarch64)
qemu
end
end
document hook-start
Binds the qemu command to the start command for aarch64 binaries.
end
define hook-run
set-aarch64
if ($aarch64)
set $isRun = 1
qemu
end
end
document hook-run
Binds the qemu command to the run command for aarch64 binaries.
end
define set-aarch64
python
if ("aarch64" in gdb.execute("info target", False, True)):
gdb.execute("set $aarch64 = 1", False, True)
else:
gdb.execute("set $aarch64 = 0", False, True)
end
end
document set-aarch64
Sets convenience variable $aarch64 if the target architecture is
aarch64.
end
python
import os.path
import re
import subprocess
class QemuCommand(gdb.Command):
"""The qemu command starts the current target using Qemu on port
1234."""
def __init__ (self):
super(QemuCommand, self).__init__("qemu", gdb.COMMAND_RUNNING,
gdb.COMPLETE_FILENAME)
def invoke (self, args, from_tty):
## Get target
it = gdb.execute("info target", False, True)
target = re.search(r"/.*', file type", it)
if target:
target = target.group(0)[:-12]
gdb.execute("set $target = \"" + target + "\"", True)
else:
raise gdb.GdbError("No symbol table loaded. " +
"Use the \"file\" command.")
## Check target architecture
if (not re.search(r"file type elf64-.*aarch64.", it)):
raise gdb.GdbError("Target binary file must be aarch64.")
## Check GDB Mode
if ("aarch64" not in
gdb.execute("show architecture", from_tty, True)):
raise gdb.GdbError("Please restart GDB with an aarch64 " +
"binary. Multiple architectures cannot \nbe debugged " +
"in the same session.")
## Kill existing processes
try:
gdb.execute("kill", False, True)
except:
pass
subprocess.call("kill -9 $(ps -u | grep -m 1 'qemu-aarch64 -g" +
" 1234' | awk '{print $2}') 2>/dev/null", shell=True)
## Reselect file
gdb.execute("file " + target, from_tty, True)
gdb.execute("set remote exec-file " + target, from_tty, True)
## Get Args
if (not args):
c = gdb.execute("show commands", False, True)
args = re.findall(r"((\n|^) *\d+ (start |run |r ).*)", c)
if (len(args)):
args = args[-1][0].strip("\n");
if (args[7:10] == "run"):
args = args[11:]
elif (args[7] == "r"):
args = args[9:]
else: # == "start"
args = args[13:]
if (not args or args == ""):
args = gdb.execute("show args", False, True)[68:-3]
gdb.execute("set args " + args, False, True)
## Start
print("Starting Qemu on Port 1234: " + target + " " + args)
subprocess.call("qemu-aarch64 -g 1234 " + target + " " + args +
" &>/dev/stdout </dev/stdin &", shell=True)
gdb.execute("target remote :1234", from_tty, True)
isRun = gdb.execute("output $isRun", from_tty, True)
if ("1" in isRun):
gdb.execute("set $isRun = 0", from_tty, True)
else:
gdb.execute("tbreak main", from_tty, True)
gdb.execute("continue", from_tty)
raise gdb.GdbError("(gdb)")
QemuCommand()
end
Emacs Note
The following part, if placed in a ~/.emacs
file will automatically
switch gud-gdb
to use gdb-multiarch
instead of gdb
. Using the
command M-x gud-gdb
is recommended for this setup.
(defcustom gud-gud-gdb-command-name "gdb-multiarch --fullname"
"Default command to run an executable under GDB in text command mode.
The option \"--fullname\" must be included in this value."
:type 'string
:group 'gud)