1use core::fmt;
4
5pub type Result<T> = core::result::Result<T, Error>;
7
8#[derive(Debug, Clone, PartialEq, Eq)]
15#[non_exhaustive]
16pub enum Error {
17 AlreadyHooked {
20 address: usize,
22 },
23 NotEnoughSpace {
26 address: usize,
28 },
29 UnrelocatableInstruction {
32 address: usize,
34 },
35 InvalidInstruction {
37 address: usize,
39 },
40 NoNearMemory {
43 address: usize,
45 },
46 NotExecutable {
48 address: usize,
50 },
51 SameAddress {
53 address: usize,
55 },
56 TargetModified {
59 address: usize,
61 },
62 ThreadNotRelocatable,
65 Assembly {
67 address: usize,
69 reason: String,
72 },
73 Os(OsError),
76 InvalidPattern {
79 pattern: String,
81 position: usize,
83 },
84 PatternNotFound {
86 pattern: String,
88 },
89 PatternNotUnique {
91 pattern: String,
93 matches: usize,
95 },
96 ModuleNotFound {
98 name: String,
100 },
101}
102
103impl fmt::Display for Error {
104 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
105 match self {
106 Error::AlreadyHooked { address } => {
107 write!(f, "address {address:#x} is already hooked")
108 }
109 Error::NotEnoughSpace { address } => {
110 write!(f, "not enough space to patch the target at {address:#x}")
111 }
112 Error::UnrelocatableInstruction { address } => {
113 write!(f, "the instruction at {address:#x} cannot be relocated")
114 }
115 Error::InvalidInstruction { address } => {
116 write!(f, "the target at {address:#x} is not a valid instruction")
117 }
118 Error::NoNearMemory { address } => {
119 write!(
120 f,
121 "no executable memory could be allocated near {address:#x}"
122 )
123 }
124 Error::NotExecutable { address } => {
125 write!(f, "the address {address:#x} is not executable")
126 }
127 Error::SameAddress { address } => {
128 write!(f, "the target and detour are the same address {address:#x}")
129 }
130 Error::TargetModified { address } => {
131 write!(f, "the code at {address:#x} was modified by a third party")
132 }
133 Error::ThreadNotRelocatable => {
134 f.write_str("a suspended thread is executing code that cannot be relocated")
135 }
136 Error::Assembly { address, reason } => {
137 write!(
138 f,
139 "the assembly for the injection at {address:#x} could not be encoded: {reason}"
140 )
141 }
142 Error::Os(_) => f.write_str("an operating system call failed"),
143 Error::InvalidPattern { pattern, position } => {
144 write!(f, "pattern `{pattern}` is invalid at position {position}")
145 }
146 Error::PatternNotFound { pattern } => {
147 write!(f, "pattern `{pattern}` matched nothing")
148 }
149 Error::PatternNotUnique { pattern, matches } => {
150 write!(f, "pattern `{pattern}` matched {matches} places, not one")
151 }
152 Error::ModuleNotFound { name } => write!(f, "module `{name}` is not loaded"),
153 }
154 }
155}
156
157impl core::error::Error for Error {
158 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
159 match self {
160 Error::Os(error) => Some(error),
161 _ => None,
162 }
163 }
164}
165
166impl Error {
167 pub(crate) fn from_detour(error: detour::Error, address: usize) -> Self {
169 match error {
170 detour::Error::SameAddress => Error::SameAddress { address },
171 detour::Error::InvalidInstruction => Error::InvalidInstruction { address },
172 detour::Error::PatchAreaTooSmall => Error::NotEnoughSpace { address },
173 detour::Error::NotExecutable => Error::NotExecutable { address },
174 detour::Error::NoNearbyMemory => Error::NoNearMemory { address },
175 detour::Error::UnsupportedInstruction => Error::UnrelocatableInstruction { address },
176 detour::Error::TargetModified => Error::TargetModified { address },
177 detour::Error::ThreadNotRelocatable => Error::ThreadNotRelocatable,
178 detour::Error::Memory(os) => Error::Os(OsError::from_detour(os, "memory protection")),
179 detour::Error::Thread(os) => Error::Os(OsError::from_detour(os, "thread suspension")),
180 other => unreachable!("detour returned an error that cannot occur here: {other:?}"),
184 }
185 }
186}
187
188#[derive(Debug, Clone, PartialEq, Eq)]
194pub struct OsError {
195 code: Option<i32>,
197 context: &'static str,
199}
200
201impl OsError {
202 pub fn raw_os_error(&self) -> Option<i32> {
205 self.code
206 }
207
208 pub(crate) fn from_detour(error: detour::OsError, context: &'static str) -> Self {
210 OsError {
211 code: error.raw_os_error(),
212 context,
213 }
214 }
215
216 pub(crate) fn from_region(error: region::Error, context: &'static str) -> Self {
218 let code = match error {
219 region::Error::SystemCall(code) | region::Error::MachCall(code) => Some(code),
220 _ => None,
221 };
222 OsError { code, context }
223 }
224
225 #[cfg(windows)]
227 pub(crate) fn last_os_error(context: &'static str) -> Self {
228 let code = unsafe { windows_sys::Win32::Foundation::GetLastError() };
230 OsError {
231 code: Some(code as i32),
232 context,
233 }
234 }
235}
236
237impl fmt::Display for OsError {
238 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
239 match self.code {
240 Some(code) => write!(
241 f,
242 "{} failed: {}",
243 self.context,
244 std::io::Error::from_raw_os_error(code)
245 ),
246 None => write!(f, "{} failed", self.context),
247 }
248 }
249}
250
251impl core::error::Error for OsError {}