1 use crate::emu::Emu;
2 use gdbstub::common::Pid;
3 use gdbstub::target;
4 use gdbstub::target::ext::extended_mode::Args;
5 use gdbstub::target::ext::extended_mode::AttachKind;
6 use gdbstub::target::ext::extended_mode::ShouldTerminate;
7 use gdbstub::target::TargetResult;
8 
9 /*=====================================
10 =            Extended Mode            =
11 =====================================*/
12 
13 // This is a stub implementation of GDB's Extended Mode extensions.
14 //
15 // Truth be told, this particular emulator is _not_ very well suited to running
16 // in extended mode, as it doesn't technically spawn/attach to any process.
17 // Nonetheless, it's useful to have a stubbed implementation in-tree which can
18 // be used for basic usability / regression testing.
19 //
20 // If you happen to implement a "proper" extended mode gdbstub, feel free to
21 // file an issue / open a PR that links to your project!
22 
23 impl target::ext::extended_mode::ExtendedMode for Emu {
kill(&mut self, pid: Option<Pid>) -> TargetResult<ShouldTerminate, Self>24     fn kill(&mut self, pid: Option<Pid>) -> TargetResult<ShouldTerminate, Self> {
25         eprintln!("GDB sent a kill request for pid {:?}", pid);
26         Ok(ShouldTerminate::No)
27     }
28 
restart(&mut self) -> Result<(), Self::Error>29     fn restart(&mut self) -> Result<(), Self::Error> {
30         eprintln!("GDB sent a restart request");
31         Ok(())
32     }
33 
attach(&mut self, pid: Pid) -> TargetResult<(), Self>34     fn attach(&mut self, pid: Pid) -> TargetResult<(), Self> {
35         eprintln!("GDB attached to a process with PID {}", pid);
36         // stub implementation: just report the same code, but running under a
37         // different pid.
38         self.reported_pid = pid;
39         Ok(())
40     }
41 
run(&mut self, filename: Option<&[u8]>, args: Args<'_, '_>) -> TargetResult<Pid, Self>42     fn run(&mut self, filename: Option<&[u8]>, args: Args<'_, '_>) -> TargetResult<Pid, Self> {
43         // simplified example: assume UTF-8 filenames / args
44         //
45         // To be 100% pedantically correct, consider converting to an `OsStr` in the
46         // least lossy way possible (e.g: using the `from_bytes` extension from
47         // `std::os::unix::ffi::OsStrExt`).
48 
49         let filename = match filename {
50             None => None,
51             Some(raw) => Some(core::str::from_utf8(raw).map_err(drop)?),
52         };
53         let args = args
54             .map(|raw| core::str::from_utf8(raw).map_err(drop))
55             .collect::<Result<Vec<_>, _>>()?;
56 
57         eprintln!(
58             "GDB tried to run a new process with filename {:?}, and args {:?}",
59             filename, args
60         );
61 
62         self.reset();
63 
64         // when running in single-threaded mode, this PID can be anything
65         Ok(Pid::new(1337).unwrap())
66     }
67 
query_if_attached(&mut self, pid: Pid) -> TargetResult<AttachKind, Self>68     fn query_if_attached(&mut self, pid: Pid) -> TargetResult<AttachKind, Self> {
69         eprintln!(
70             "GDB queried if it was attached to a process with PID {}",
71             pid
72         );
73         Ok(AttachKind::Attach)
74     }
75 
76     #[inline(always)]
support_configure_aslr( &mut self, ) -> Option<target::ext::extended_mode::ConfigureAslrOps<'_, Self>>77     fn support_configure_aslr(
78         &mut self,
79     ) -> Option<target::ext::extended_mode::ConfigureAslrOps<'_, Self>> {
80         Some(self)
81     }
82 
83     #[inline(always)]
support_configure_env( &mut self, ) -> Option<target::ext::extended_mode::ConfigureEnvOps<'_, Self>>84     fn support_configure_env(
85         &mut self,
86     ) -> Option<target::ext::extended_mode::ConfigureEnvOps<'_, Self>> {
87         Some(self)
88     }
89 
90     #[inline(always)]
support_configure_startup_shell( &mut self, ) -> Option<target::ext::extended_mode::ConfigureStartupShellOps<'_, Self>>91     fn support_configure_startup_shell(
92         &mut self,
93     ) -> Option<target::ext::extended_mode::ConfigureStartupShellOps<'_, Self>> {
94         Some(self)
95     }
96 
97     #[inline(always)]
support_configure_working_dir( &mut self, ) -> Option<target::ext::extended_mode::ConfigureWorkingDirOps<'_, Self>>98     fn support_configure_working_dir(
99         &mut self,
100     ) -> Option<target::ext::extended_mode::ConfigureWorkingDirOps<'_, Self>> {
101         Some(self)
102     }
103 
104     #[inline(always)]
support_current_active_pid( &mut self, ) -> Option<target::ext::extended_mode::CurrentActivePidOps<'_, Self>>105     fn support_current_active_pid(
106         &mut self,
107     ) -> Option<target::ext::extended_mode::CurrentActivePidOps<'_, Self>> {
108         Some(self)
109     }
110 }
111 
112 impl target::ext::extended_mode::ConfigureAslr for Emu {
cfg_aslr(&mut self, enabled: bool) -> TargetResult<(), Self>113     fn cfg_aslr(&mut self, enabled: bool) -> TargetResult<(), Self> {
114         eprintln!("GDB {} ASLR", if enabled { "enabled" } else { "disabled" });
115         Ok(())
116     }
117 }
118 
119 impl target::ext::extended_mode::ConfigureEnv for Emu {
set_env(&mut self, key: &[u8], val: Option<&[u8]>) -> TargetResult<(), Self>120     fn set_env(&mut self, key: &[u8], val: Option<&[u8]>) -> TargetResult<(), Self> {
121         // simplified example: assume UTF-8 key/val env vars
122         let key = core::str::from_utf8(key).map_err(drop)?;
123         let val = match val {
124             None => None,
125             Some(raw) => Some(core::str::from_utf8(raw).map_err(drop)?),
126         };
127 
128         eprintln!("GDB tried to set a new env var: {:?}={:?}", key, val);
129 
130         Ok(())
131     }
132 
remove_env(&mut self, key: &[u8]) -> TargetResult<(), Self>133     fn remove_env(&mut self, key: &[u8]) -> TargetResult<(), Self> {
134         let key = core::str::from_utf8(key).map_err(drop)?;
135         eprintln!("GDB tried to set remove a env var: {:?}", key);
136 
137         Ok(())
138     }
139 
reset_env(&mut self) -> TargetResult<(), Self>140     fn reset_env(&mut self) -> TargetResult<(), Self> {
141         eprintln!("GDB tried to reset env vars");
142 
143         Ok(())
144     }
145 }
146 
147 impl target::ext::extended_mode::ConfigureStartupShell for Emu {
cfg_startup_with_shell(&mut self, enabled: bool) -> TargetResult<(), Self>148     fn cfg_startup_with_shell(&mut self, enabled: bool) -> TargetResult<(), Self> {
149         eprintln!(
150             "GDB {} startup with shell",
151             if enabled { "enabled" } else { "disabled" }
152         );
153         Ok(())
154     }
155 }
156 
157 impl target::ext::extended_mode::ConfigureWorkingDir for Emu {
cfg_working_dir(&mut self, dir: Option<&[u8]>) -> TargetResult<(), Self>158     fn cfg_working_dir(&mut self, dir: Option<&[u8]>) -> TargetResult<(), Self> {
159         let dir = match dir {
160             None => None,
161             Some(raw) => Some(core::str::from_utf8(raw).map_err(drop)?),
162         };
163 
164         match dir {
165             None => eprintln!("GDB reset the working directory"),
166             Some(dir) => eprintln!("GDB set the working directory to {:?}", dir),
167         }
168 
169         Ok(())
170     }
171 }
172 
173 impl target::ext::extended_mode::CurrentActivePid for Emu {
current_active_pid(&mut self) -> Result<Pid, Self::Error>174     fn current_active_pid(&mut self) -> Result<Pid, Self::Error> {
175         Ok(self.reported_pid)
176     }
177 }
178