-
-
Notifications
You must be signed in to change notification settings - Fork 50
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add ARM Cortex-M OpenOCD arch, command, and session manager (#83)
This is for debugging ARM cortex-m targets through JTAG/SWD using the gdbserver implemented in OpenOCD. Manually tested with a debugger and openocd
- Loading branch information
1 parent
eac4c06
commit 694924f
Showing
4 changed files
with
216 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
""" | ||
ARM through OpenOCD support for GEF | ||
To use, source this file *after* gef | ||
Author: Grazfather | ||
""" | ||
|
||
from typing import Optional | ||
from pathlib import Path | ||
|
||
import gdb | ||
|
||
assert 'gef' in globals(), "This file must be source after gef.py" | ||
|
||
|
||
class ARMOpenOCD(ARM): | ||
arch = "ARMOpenOCD" | ||
aliases = ("ARMOpenOCD",) | ||
all_registers = ("$r0", "$r1", "$r2", "$r3", "$r4", "$r5", "$r6", | ||
"$r7", "$r8", "$r9", "$r10", "$r11", "$r12", "$sp", | ||
"$lr", "$pc", "$xPSR") | ||
flag_register = "$xPSR" | ||
@staticmethod | ||
def supports_gdb_arch(arch: str) -> Optional[bool]: | ||
if "arm" in arch and arch.endswith("-m"): | ||
return True | ||
return None | ||
|
||
@staticmethod | ||
def maps(): | ||
yield from GefMemoryManager.parse_info_mem() | ||
|
||
|
||
@register | ||
class OpenOCDRemoteCommand(GenericCommand): | ||
"""This command is intended to replace `gef-remote` to connect to an | ||
OpenOCD-hosted gdbserver. It uses a special session manager that knows how | ||
to connect and manage the server.""" | ||
|
||
_cmdline_ = "gef-openocd-remote" | ||
_syntax_ = f"{_cmdline_} [OPTIONS] HOST PORT" | ||
_example_ = [f"{_cmdline_} --file /path/to/binary.elf localhost 3333", | ||
f"{_cmdline_} localhost 3333"] | ||
|
||
def __init__(self) -> None: | ||
super().__init__(prefix=False) | ||
return | ||
|
||
@parse_arguments({"host": "", "port": 0}, {"--file": ""}) | ||
def do_invoke(self, _: list[str], **kwargs: Any) -> None: | ||
if gef.session.remote is not None: | ||
err("You're already in a remote session. Close it first before opening a new one...") | ||
return | ||
|
||
# argument check | ||
args: argparse.Namespace = kwargs["arguments"] | ||
if not args.host or not args.port: | ||
err("Missing parameters") | ||
return | ||
|
||
# Try to establish the remote session, throw on error | ||
# Set `.remote_initializing` to True here - `GefRemoteSessionManager` invokes code which | ||
# calls `is_remote_debug` which checks if `remote_initializing` is True or `.remote` is None | ||
# This prevents some spurious errors being thrown during startup | ||
gef.session.remote_initializing = True | ||
session = GefOpenOCDRemoteSessionManager(args.host, args.port, args.file) | ||
|
||
dbg(f"[remote] initializing remote session with {session.target} under {session.root}") | ||
|
||
# Connect can return false if it wants us to disconnect | ||
if not session.connect(): | ||
gef.session.remote = None | ||
gef.session.remote_initializing = False | ||
return | ||
if not session.setup(): | ||
gef.session.remote = None | ||
gef.session.remote_initializing = False | ||
raise EnvironmentError("Failed to setup remote target") | ||
|
||
gef.session.remote_initializing = False | ||
gef.session.remote = session | ||
reset_all_caches() | ||
gdb.execute("context") | ||
return | ||
|
||
|
||
# We CANNOT use the normal session manager because it assumes we have a PID | ||
class GefOpenOCDRemoteSessionManager(GefRemoteSessionManager): | ||
"""This subclass of GefRemoteSessionManager specially handles the | ||
intricacies involved with connecting to an OpenOCD-hosted GDB server. | ||
Specifically, it does not have the concept of PIDs which we need to work | ||
around.""" | ||
def __init__(self, host: str, port: str, file: str="") -> None: | ||
self.__host = host | ||
self.__port = port | ||
self.__file = file | ||
self.__local_root_fd = tempfile.TemporaryDirectory() | ||
self.__local_root_path = Path(self.__local_root_fd.name) | ||
class OpenOCDMode(): | ||
def prompt_string(self) -> str: | ||
return Color.boldify("(OpenOCD) ") | ||
|
||
self._mode = OpenOCDMode() | ||
|
||
def __str__(self) -> str: | ||
return f"OpenOCDRemoteSessionManager(='{self.__tty}', file='{self.__file}', attach={self.__attach})" | ||
|
||
def close(self) -> None: | ||
self.__local_root_fd.cleanup() | ||
try: | ||
gef_on_new_unhook(self.remote_objfile_event_handler) | ||
gef_on_new_hook(new_objfile_handler) | ||
except Exception as e: | ||
warn(f"Exception while restoring local context: {str(e)}") | ||
return | ||
|
||
@property | ||
def target(self) -> str: | ||
return f"{self.__host}:{self.__port}" | ||
|
||
@property | ||
def root(self) -> Path: | ||
return self.__local_root_path.absolute() | ||
|
||
def sync(self, src: str, dst: Optional[str] = None) -> bool: | ||
# We cannot sync from this target | ||
return None | ||
|
||
@property | ||
def file(self) -> Optional[Path]: | ||
if self.__file: | ||
return Path(self.__file).expanduser() | ||
return None | ||
|
||
def connect(self) -> bool: | ||
"""Connect to remote target. If in extended mode, also attach to the given PID.""" | ||
# before anything, register our new hook to download files from the remote target | ||
dbg(f"[remote] Installing new objfile handlers") | ||
try: | ||
gef_on_new_unhook(new_objfile_handler) | ||
except SystemError: | ||
# the default objfile handler might already have been removed, ignore failure | ||
pass | ||
|
||
gef_on_new_hook(self.remote_objfile_event_handler) | ||
|
||
# Connect | ||
with DisableContextOutputContext(): | ||
self._gdb_execute(f"target extended-remote {self.target}") | ||
|
||
try: | ||
with DisableContextOutputContext(): | ||
if self.file: | ||
self._gdb_execute(f"file '{self.file}'") | ||
except Exception as e: | ||
err(f"Failed to connect to {self.target}: {e}") | ||
# a failure will trigger the cleanup, deleting our hook | ||
return False | ||
|
||
return True | ||
|
||
def setup(self) -> bool: | ||
dbg(f"Setting up as remote session") | ||
|
||
# refresh gef to consider the binary | ||
reset_all_caches() | ||
if self.file: | ||
gef.binary = Elf(self.file) | ||
# We'd like to set this earlier, but we can't because of this bug | ||
# https://sourceware.org/bugzilla/show_bug.cgi?id=31303 | ||
reset_architecture("ARMOpenOCD") | ||
return True | ||
|
||
def _gdb_execute(self, cmd): | ||
dbg(f"[remote] Executing '{cmd}'") | ||
gdb.execute(cmd) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
## ARMOpenOCD | ||
|
||
The ARM OpenOCD architecture is a special arcthtecture used with the `gef-openocd-remote` command. | ||
Please read the [documentation](../commands/gef-openocd-remote.md) for the command. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
## Command gef-openocd-remote | ||
|
||
The `gef-openocd-command` is used with the [`ARMOpenOCD`](../../archs/arm-openocd.py] architecture. | ||
|
||
The [arm-openocd.py](../../archs/arm-openocd.py) script adds an easy way to extend the `gef-remote` | ||
functionality to easily debug ARM targets using a OpenOCD gdbserver. It creates a custom ARM-derived | ||
`Architecture`, as well as the `gef-openocd-remote` command, which lets you easily connect to the | ||
target, optionally loading the accompanying ELF binary. | ||
|
||
### Usage | ||
|
||
```bash | ||
gef-openocd-remote localhost 3333 --file /path/to/elf | ||
``` |