Skip to content

Commit c9c186e

Browse files
committed
Add implementation of PTRACE_{GET,SET}REGSET
Also added `PTRACE_{GET,SET}REGS` for most platforms other than x86 using aforementioned implementation.
1 parent c6f9e23 commit c9c186e

File tree

2 files changed

+135
-5
lines changed

2 files changed

+135
-5
lines changed

src/sys/ptrace/linux.rs

Lines changed: 113 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@ pub type AddressType = *mut ::libc::c_void;
1717
target_arch = "x86_64",
1818
any(target_env = "gnu", target_env = "musl")
1919
),
20-
all(target_arch = "x86", target_env = "gnu")
21-
)
20+
all(target_arch = "x86", target_env = "gnu"),
21+
all(target_arch = "aarch64", target_env = "gnu"),
22+
all(target_arch = "riscv64", target_env = "gnu"),
23+
),
2224
))]
2325
use libc::user_regs_struct;
2426

@@ -152,6 +154,29 @@ libc_enum! {
152154
}
153155
}
154156

157+
libc_enum! {
158+
#[cfg(all(
159+
target_os = "linux",
160+
target_env = "gnu",
161+
any(
162+
target_arch = "x86_64",
163+
target_arch = "x86",
164+
target_arch = "aarch64",
165+
target_arch = "riscv64",
166+
)
167+
))]
168+
#[repr(i32)]
169+
/// Defining a specific register set, as used in [`getregset`] and [`setregset`].
170+
#[non_exhaustive]
171+
pub enum RegisterSet {
172+
NT_PRSTATUS,
173+
NT_PRFPREG,
174+
NT_PRPSINFO,
175+
NT_TASKSTRUCT,
176+
NT_AUXV,
177+
}
178+
}
179+
155180
libc_bitflags! {
156181
/// Ptrace options used in conjunction with the PTRACE_SETOPTIONS request.
157182
/// See `man ptrace` for more details.
@@ -213,6 +238,48 @@ pub fn getregs(pid: Pid) -> Result<user_regs_struct> {
213238
ptrace_get_data::<user_regs_struct>(Request::PTRACE_GETREGS, pid)
214239
}
215240

241+
/// Get user registers, as with `ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, ...)`
242+
#[cfg(all(
243+
target_os = "linux",
244+
target_env = "gnu",
245+
any(
246+
target_arch = "aarch64",
247+
target_arch = "riscv64",
248+
)
249+
))]
250+
pub fn getregs(pid: Pid) -> Result<user_regs_struct> {
251+
getregset(pid, RegisterSet::NT_PRSTATUS)
252+
}
253+
254+
/// Get a particular set of user registers, as with `ptrace(PTRACE_GETREGSET, ...)`
255+
#[cfg(all(
256+
target_os = "linux",
257+
target_env = "gnu",
258+
any(
259+
target_arch = "x86_64",
260+
target_arch = "x86",
261+
target_arch = "aarch64",
262+
target_arch = "riscv64",
263+
)
264+
))]
265+
pub fn getregset(pid: Pid, set: RegisterSet) -> Result<user_regs_struct> {
266+
let request = Request::PTRACE_GETREGSET;
267+
let mut data = mem::MaybeUninit::<user_regs_struct>::uninit();
268+
let mut iov = libc::iovec {
269+
iov_base: data.as_mut_ptr() as *mut _,
270+
iov_len: mem::size_of::<user_regs_struct>(),
271+
};
272+
unsafe {
273+
ptrace_other(
274+
request,
275+
pid,
276+
set as i32 as _,
277+
&mut iov as *mut _ as *mut _,
278+
)?;
279+
};
280+
Ok(unsafe { data.assume_init() })
281+
}
282+
216283
/// Set user registers, as with `ptrace(PTRACE_SETREGS, ...)`
217284
#[cfg(all(
218285
target_os = "linux",
@@ -236,6 +303,50 @@ pub fn setregs(pid: Pid, regs: user_regs_struct) -> Result<()> {
236303
Errno::result(res).map(drop)
237304
}
238305

306+
/// Set user registers, as with `ptrace(PTRACE_SETREGSET, pid, NT_PRSTATUS, ...)`
307+
#[cfg(all(
308+
target_os = "linux",
309+
target_env = "gnu",
310+
any(
311+
target_arch = "aarch64",
312+
target_arch = "riscv64",
313+
)
314+
))]
315+
pub fn setregs(pid: Pid, regs: user_regs_struct) -> Result<()> {
316+
setregset(pid, RegisterSet::NT_PRSTATUS, regs)
317+
}
318+
319+
/// Set a particular set of user registers, as with `ptrace(PTRACE_SETREGSET, ...)`
320+
#[cfg(all(
321+
target_os = "linux",
322+
target_env = "gnu",
323+
any(
324+
target_arch = "x86_64",
325+
target_arch = "x86",
326+
target_arch = "aarch64",
327+
target_arch = "riscv64",
328+
)
329+
))]
330+
pub fn setregset(
331+
pid: Pid,
332+
set: RegisterSet,
333+
regs: user_regs_struct,
334+
) -> Result<()> {
335+
let iov = libc::iovec {
336+
iov_base: &regs as *const _ as *mut _,
337+
iov_len: mem::size_of::<user_regs_struct>(),
338+
};
339+
unsafe {
340+
ptrace_other(
341+
Request::PTRACE_SETREGSET,
342+
pid,
343+
set as i32 as _,
344+
&iov as *const _ as *mut _,
345+
)?;
346+
}
347+
Ok(())
348+
}
349+
239350
/// Function for ptrace requests that return values from the data field.
240351
/// Some ptrace get requests populate structs or larger elements than `c_long`
241352
/// and therefore use the data field to return values. This function handles these

test/sys/test_ptrace.rs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -179,8 +179,13 @@ fn test_ptrace_interrupt() {
179179
// ptrace::{setoptions, getregs} are only available in these platforms
180180
#[cfg(all(
181181
target_os = "linux",
182-
any(target_arch = "x86_64", target_arch = "x86"),
183-
target_env = "gnu"
182+
target_env = "gnu",
183+
any(
184+
target_arch = "x86_64",
185+
target_arch = "x86",
186+
target_arch = "aarch64",
187+
target_arch = "riscv64",
188+
)
184189
))]
185190
#[test]
186191
fn test_ptrace_syscall() {
@@ -226,14 +231,28 @@ fn test_ptrace_syscall() {
226231
let get_syscall_id =
227232
|| ptrace::getregs(child).unwrap().orig_eax as libc::c_long;
228233

234+
#[cfg(target_arch = "riscv64")]
235+
let get_syscall_id =
236+
|| ptrace::getregs(child).unwrap().regs[8] as libc::c_long;
237+
238+
#[cfg(target_arch = "riscv64")]
239+
let get_syscall_id =
240+
|| ptrace::getregs(child).unwrap().a7 as libc::c_long;
241+
229242
// this duplicates `get_syscall_id` for the purpose of testing `ptrace::read_user`.
230243
#[cfg(target_arch = "x86_64")]
231244
let rax_offset = offset_of!(libc::user_regs_struct, orig_rax);
232245
#[cfg(target_arch = "x86")]
233246
let rax_offset = offset_of!(libc::user_regs_struct, orig_eax);
247+
#[cfg(target_arch = "aarch64")]
248+
let rax_offset = offset_of!(libc::user_regs_struct, regs)
249+
+ 8 * mem::size_of::<libc::c_ulonglong>();
250+
#[cfg(target_arch = "riscv64")]
251+
let rax_offset = offset_of!(libc::user_regs_struct, a7);
234252

235253
let get_syscall_from_user_area = || {
236-
// Find the offset of `user.regs.rax` (or `user.regs.eax` for x86)
254+
// Find the offset of `user.regs.rax` (or `eax` for x86, `x8`
255+
// for aarch64, and `a7` for RISC-V)
237256
let rax_offset = offset_of!(libc::user, regs) + rax_offset;
238257
ptrace::read_user(child, rax_offset as _).unwrap()
239258
as libc::c_long

0 commit comments

Comments
 (0)