Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

libdrgn: add support for remote debugging via OpenOCD #338

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions _drgn.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -837,6 +837,11 @@ class ProgramFlags(enum.Flag):
kernel or a running process).
"""

IS_LOCAL = ...
"""
The program is running on the local machine.
"""

class FindObjectFlags(enum.Flag):
"""
``FindObjectFlags`` are flags for :meth:`Program.object()`. These can be
Expand Down
37 changes: 37 additions & 0 deletions docs/user_guide.rst
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,43 @@ explicitly::
int counter;
} atomic_t

Remote Debugging
^^^^^^^^^^^^^^^^

drgn supports remote kernel debugging using `OpenOCD
<https://www.openocd.org/>`_ as an interface to a JTAG/SWD debug adapter
connected to a remote target. To use the remote debugging feature,
you must have the following information about your target.

* A suitable OpenOCD configuration file for your target.
* The name of the Test Access Point (TAP) which may be used to access the
physical memory where the kernel is running. The TAP name will
be specified in the OpenOCD configuration file.
* The load address of the kernel in the target's physical memory.

You must also have debugging symbols for the kernel running on your target
(i.e. the ``vmlinux`` file).

To begin the debugging session, ensure that your target is running the
kernel and that OpenOCD is running and connected to your target. Then
issue the following command, making the necessary substitutions::

$ drgn --openocd /path/to/vmlinux --openocd-tap $TAP_NAME --openocd-addr $LOAD_ADDR

Debug information for the specified kernel will be loaded automatically,
but there is currently no support for automatically loading debug
information for loaded kernel modules. Debug information for modules
must be loaded manually using :meth:`drgn.Program.load_debug_info()`.

drgn may also be used to debug microcontroller firmware and other
bare-metal MMU-less programs. To do this, you must have debugging
symbols for your firmware. To begin debugging, ensure that your target
is running the firmware and that OpenOCD is running and connected to
your target. Then issue the following command, making the necessary
substitutions::

$ drgn --openocd /path/to/firmware.elf --openocd-tap $TAP_NAME --openocd-nommu

Next Steps
----------

Expand Down
30 changes: 30 additions & 0 deletions drgn/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ def _main() -> None:
program_group.add_argument(
"-k", "--kernel", action="store_true", help="debug the running kernel (default)"
)
program_group.add_argument(
"--openocd", metavar="PATH", type=str, help="debug the given kernel on a remote target using OpenOCD"
)
program_group.add_argument(
"-c", "--core", metavar="PATH", type=str, help="debug the given core dump"
)
Expand Down Expand Up @@ -137,6 +140,23 @@ def _main() -> None:
help="don't load any debugging symbols that were not explicitly added with -s",
)

openocd_group = parser.add_argument_group(title="OpenOCD options")
openocd_group.add_argument(
"--openocd-host", metavar="HOST", type=str, default="localhost", help="hostname of OpenOCD server"
)
openocd_group.add_argument(
"--openocd-port", metavar="PORT", type=str, default="6666", help="TCL port number of OpenOCD server"
)
openocd_group.add_argument(
"--openocd-addr", metavar="ADDR", type=lambda x: int(x,0), help="debug kernel at given physical address"
)
openocd_group.add_argument(
"--openocd-nommu", action="store_true", help="debug a program that does not use the MMU"
)
openocd_group.add_argument(
"--openocd-tap", metavar="TAP", type=str, help="debug kernel using given TAP"
)

parser.add_argument(
"-q",
"--quiet",
Expand Down Expand Up @@ -182,6 +202,16 @@ def _main() -> None:
prog.set_core_dump(args.core)
elif args.pid is not None:
prog.set_pid(args.pid or os.getpid())
elif args.openocd:
if args.openocd_tap is None:
print("error: --openocd-tap required when using OpenOCD", file=sys.stderr)
sys.exit(1)
Comment on lines +207 to +208
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tiny suggestion, Python allows you to shorten this:

              sys.exit("error: --openocd-tap required when using OpenOCD")

Non-int args are printed to stderr and an error code of 1 is returned.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ack, I'll take care of that when rebasing.

if args.openocd_addr is None and args.openocd_nommu is None:
print("error: --openocd-addr or --openocd-nommu required when using OpenOCD", file=sys.stderr)
sys.exit(1)
prog.set_openocd(vmlinux=args.openocd, host=args.openocd_host,
port=args.openocd_port, tap=args.openocd_tap,
mmu=not args.openocd_nommu, paddr=args.openocd_addr or 0)
else:
prog.set_kernel()
except PermissionError as e:
Expand Down
Loading