Skip to content

Commit 4cdfad5

Browse files
committed
wip
1 parent faf859e commit 4cdfad5

File tree

1 file changed

+149
-0
lines changed

1 file changed

+149
-0
lines changed

archs/arm-openocd.py

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@
66
Author: Grazfather
77
"""
88

9+
from typing import Optional
10+
11+
import gdb
12+
13+
914
class ARMOpenOCD(ARM):
1015
arch = "ARMOpenOCD"
1116
aliases = ("ARMOpenOCD",)
@@ -18,3 +23,147 @@ def supports_gdb_arch(arch: str) -> Optional[bool]:
1823
if "arm" in arch and arch.endswith("-m"):
1924
return True
2025
return None
26+
27+
@staticmethod
28+
def maps():
29+
yield from GefMemoryManager.parse_info_mem()
30+
31+
32+
@register
33+
class OpenOCDRemoteCommand(GenericCommand):
34+
"""This command is intended to replace `gef-remote` to connect to an
35+
OpenOCD-hosted gdbserver. It uses a special session manager that knows how
36+
to connect and manage the server."""
37+
38+
_cmdline_ = "gef-openocd-remote"
39+
_syntax_ = f"{_cmdline_} [OPTIONS] HOST PORT"
40+
_example_ = [f"{_cmdline_} --file /path/to/binary.elf localhost 3333",
41+
f"{_cmdline_} localhost 3333"]
42+
43+
def __init__(self) -> None:
44+
super().__init__(prefix=False)
45+
return
46+
47+
@parse_arguments({"host": "", "port": ""}, {"--file": ""})
48+
def do_invoke(self, _: List[str], **kwargs: Any) -> None:
49+
if gef.session.remote is not None:
50+
err("You're already in a remote session. Close it first before opening a new one...")
51+
return
52+
53+
# argument check
54+
args: argparse.Namespace = kwargs["arguments"]
55+
if not args.host or not args.port:
56+
err("Missing parameters")
57+
return
58+
59+
# Try to establish the remote session, throw on error
60+
# Set `.remote_initializing` to True here - `GefRemoteSessionManager` invokes code which
61+
# calls `is_remote_debug` which checks if `remote_initializing` is True or `.remote` is None
62+
# This prevents some spurious errors being thrown during startup
63+
gef.session.remote_initializing = True
64+
session = GefOpenOCDRemoteSessionManager(args.host, args.port, args.file)
65+
66+
dbg(f"[remote] initializing remote session with {session.target} under {session.root}")
67+
68+
# Connect can return false if it wants us to disconnect
69+
if not session.connect():
70+
gef.session.remote = None
71+
gef.session.remote_initializing = False
72+
return
73+
if not session.setup():
74+
gef.session.remote = None
75+
gef.session.remote_initializing = False
76+
raise EnvironmentError("Failed to setup remote target")
77+
78+
gef.session.remote_initializing = False
79+
gef.session.remote = session
80+
reset_all_caches()
81+
gdb.execute("context")
82+
return
83+
84+
85+
# We CANNOT use the normal session manager because it assumes we have a PID
86+
class GefOpenOCDRemoteSessionManager(GefRemoteSessionManager):
87+
"""This subclass of GefRemoteSessionManager specially handles the
88+
intricacies involved with connecting to an OpenOCD-hosted GDB server.
89+
Specifically, it does not have the concept of PIDs which we need to work
90+
around."""
91+
def __init__(self, host, port, file: str="") -> None:
92+
self.__host = host
93+
self.__port = port
94+
self.__file = file
95+
self.__local_root_fd = tempfile.TemporaryDirectory()
96+
self.__local_root_path = pathlib.Path(self.__local_root_fd.name)
97+
98+
def __str__(self) -> str:
99+
return f"OpenOCDRemoteSessionManager(='{self.__tty}', file='{self.__file}', attach={self.__attach})"
100+
101+
def close(self) -> None:
102+
self.__local_root_fd.cleanup()
103+
try:
104+
gef_on_new_unhook(self.remote_objfile_event_handler)
105+
gef_on_new_hook(new_objfile_handler)
106+
except Exception as e:
107+
warn(f"Exception while restoring local context: {str(e)}")
108+
return
109+
110+
@property
111+
def target(self) -> str:
112+
return f"{self.__host}:{self.__port}"
113+
114+
@property
115+
def root(self) -> pathlib.Path:
116+
return self.__local_root_path.absolute()
117+
118+
def sync(self, src: str, dst: Optional[str] = None) -> bool:
119+
# We cannot sync from this target
120+
return None
121+
122+
@property
123+
def file(self) -> Optional[pathlib.Path]:
124+
if self.__file:
125+
return pathlib.Path(self.__file).expanduser()
126+
return None
127+
128+
def connect(self) -> bool:
129+
"""Connect to remote target. If in extended mode, also attach to the given PID."""
130+
# before anything, register our new hook to download files from the remote target
131+
dbg(f"[remote] Installing new objfile handlers")
132+
try:
133+
gef_on_new_unhook(new_objfile_handler)
134+
except SystemError:
135+
# the default objfile handler might already have been removed, ignore failure
136+
pass
137+
138+
gef_on_new_hook(self.remote_objfile_event_handler)
139+
140+
# Connect
141+
with DisableContextOutputContext():
142+
self._gdb_execute(f"target extended-remote {self.target}")
143+
144+
try:
145+
with DisableContextOutputContext():
146+
if self.file:
147+
self._gdb_execute(f"file {self.file}")
148+
except Exception as e:
149+
err(f"Failed to connect to {self.target}: {e}")
150+
# a failure will trigger the cleanup, deleting our hook
151+
return False
152+
153+
return True
154+
155+
def setup(self) -> bool:
156+
dbg(f"Setting up as remote session")
157+
158+
# refresh gef to consider the binary
159+
reset_all_caches()
160+
if self.file:
161+
gef.binary = Elf(self.file)
162+
# We'd like to set this earlier, but we can't because of this bug
163+
# https://sourceware.org/bugzilla/show_bug.cgi?id=31303
164+
reset_architecture("ARMOpenOCD")
165+
return True
166+
167+
def _gdb_execute(self, cmd):
168+
dbg(f"[remote] Executing '{cmd}'")
169+
gdb.execute(cmd)

0 commit comments

Comments
 (0)