From ee712452b79ed2e71194322647796abbb7c5f881 Mon Sep 17 00:00:00 2001 From: Alex Knauth Date: Wed, 1 Nov 2023 11:38:19 -0400 Subject: [PATCH] Add process_get_module_path (#735) --- crates/livesplit-auto-splitting/README.md | 17 +++++++++ crates/livesplit-auto-splitting/src/lib.rs | 17 +++++++++ .../livesplit-auto-splitting/src/process.rs | 9 +++++ .../livesplit-auto-splitting/src/runtime.rs | 35 +++++++++++++++++++ src/auto_splitting/mod.rs | 17 +++++++++ 5 files changed, 95 insertions(+) diff --git a/crates/livesplit-auto-splitting/README.md b/crates/livesplit-auto-splitting/README.md index 7e13d00f..df78fe00 100644 --- a/crates/livesplit-auto-splitting/README.md +++ b/crates/livesplit-auto-splitting/README.md @@ -157,6 +157,23 @@ extern "C" { name_ptr: *const u8, name_len: usize, ) -> Option; + /// Stores the file system path of a module in a process in the buffer + /// given. The pointer to the module name needs to point to valid UTF-8 + /// encoded text with the given length. The path is a path that is + /// accessible through the WASI file system, so a Windows path of + /// `C:\foo\bar.exe` would be returned as `/mnt/c/foo/bar.exe`. Returns + /// `false` if the buffer is too small. After this call, no matter whether + /// it was successful or not, the `buf_len_ptr` will be set to the required + /// buffer size. If `false` is returned and the `buf_len_ptr` got set to 0, + /// the path or the module does not exist or it failed to get read. The path + /// is guaranteed to be valid UTF-8 and is not nul-terminated. + pub fn process_get_module_path( + process: Process, + name_ptr: *const u8, + name_len: usize, + buf_ptr: *mut u8, + buf_len_ptr: *mut usize, + ) -> bool; /// Stores the file system path of the executable in the buffer given. The /// path is a path that is accessible through the WASI file system, so a /// Windows path of `C:\foo\bar.exe` would be returned as diff --git a/crates/livesplit-auto-splitting/src/lib.rs b/crates/livesplit-auto-splitting/src/lib.rs index 4a85d7c2..840640ad 100644 --- a/crates/livesplit-auto-splitting/src/lib.rs +++ b/crates/livesplit-auto-splitting/src/lib.rs @@ -157,6 +157,23 @@ //! name_ptr: *const u8, //! name_len: usize, //! ) -> Option; +//! /// Stores the file system path of a module in a process in the buffer +//! /// given. The pointer to the module name needs to point to valid UTF-8 +//! /// encoded text with the given length. The path is a path that is +//! /// accessible through the WASI file system, so a Windows path of +//! /// `C:\foo\bar.exe` would be returned as `/mnt/c/foo/bar.exe`. Returns +//! /// `false` if the buffer is too small. After this call, no matter whether +//! /// it was successful or not, the `buf_len_ptr` will be set to the required +//! /// buffer size. If `false` is returned and the `buf_len_ptr` got set to 0, +//! /// the path or the module does not exist or it failed to get read. The path +//! /// is guaranteed to be valid UTF-8 and is not nul-terminated. +//! pub fn process_get_module_path( +//! process: Process, +//! name_ptr: *const u8, +//! name_len: usize, +//! buf_ptr: *mut u8, +//! buf_len_ptr: *mut usize, +//! ) -> bool; //! /// Stores the file system path of the executable in the buffer given. The //! /// path is a path that is accessible through the WASI file system, so a //! /// Windows path of `C:\foo\bar.exe` would be returned as diff --git a/crates/livesplit-auto-splitting/src/process.rs b/crates/livesplit-auto-splitting/src/process.rs index 0f932680..42f82a34 100644 --- a/crates/livesplit-auto-splitting/src/process.rs +++ b/crates/livesplit-auto-splitting/src/process.rs @@ -141,6 +141,15 @@ impl Process { .sum()) } + pub(super) fn module_path(&mut self, module: &str) -> Result, ModuleError> { + self.refresh_memory_ranges()?; + self.memory_ranges + .iter() + .find(|m| m.filename().is_some_and(|f| f.ends_with(module))) + .context(ModuleDoesntExist) + .map(|m| build_path(m.filename().unwrap()).unwrap_or_default()) + } + pub(super) fn read_mem(&self, address: Address, buf: &mut [u8]) -> io::Result<()> { self.handle.copy_address(address as usize, buf) } diff --git a/crates/livesplit-auto-splitting/src/runtime.rs b/crates/livesplit-auto-splitting/src/runtime.rs index 0b044aed..e5dbb4fa 100644 --- a/crates/livesplit-auto-splitting/src/runtime.rs +++ b/crates/livesplit-auto-splitting/src/runtime.rs @@ -954,6 +954,41 @@ fn bind_interface(linker: &mut Linker>) -> Result<(), Creat source, name: "process_get_path", })? + .func_wrap("env", "process_get_module_path", { + |mut caller: Caller<'_, Context>, + process: u64, + name_ptr: u32, + name_len: u32, + path_ptr: u32, + path_len_ptr: u32| { + let (memory, context) = memory_and_context(&mut caller); + let module_name = get_str(memory, name_ptr, name_len)?; + let path = context + .processes + .get_mut(ProcessKey::from(KeyData::from_ffi(process))) + .ok_or_else(|| format_err!("Invalid process handle: {process}"))? + .module_path(module_name); + + let path_len_bytes = get_arr_mut(memory, path_len_ptr)?; + if let Ok(path) = path { + let path_len = u32::from_le_bytes(*path_len_bytes) as usize; + *path_len_bytes = (path.len() as u32).to_le_bytes(); + if path_len < path.len() { + return Ok(0u32); + } + let buf = get_slice_mut(memory, path_ptr, path.len() as _)?; + buf.copy_from_slice(path.as_bytes()); + Ok(1u32) + } else { + *path_len_bytes = 0u32.to_le_bytes(); + Ok(0u32) + } + } + }) + .map_err(|source| CreationError::LinkFunction { + source, + name: "process_get_module_path", + })? .func_wrap("env", "process_read", { |mut caller: Caller<'_, Context>, process: u64, diff --git a/src/auto_splitting/mod.rs b/src/auto_splitting/mod.rs index a55812f5..e41e852b 100644 --- a/src/auto_splitting/mod.rs +++ b/src/auto_splitting/mod.rs @@ -157,6 +157,23 @@ //! name_ptr: *const u8, //! name_len: usize, //! ) -> Option; +//! /// Stores the file system path of a module in a process in the buffer +//! /// given. The pointer to the module name needs to point to valid UTF-8 +//! /// encoded text with the given length. The path is a path that is +//! /// accessible through the WASI file system, so a Windows path of +//! /// `C:\foo\bar.exe` would be returned as `/mnt/c/foo/bar.exe`. Returns +//! /// `false` if the buffer is too small. After this call, no matter whether +//! /// it was successful or not, the `buf_len_ptr` will be set to the required +//! /// buffer size. If `false` is returned and the `buf_len_ptr` got set to 0, +//! /// the path or the module does not exist or it failed to get read. The path +//! /// is guaranteed to be valid UTF-8 and is not nul-terminated. +//! pub fn process_get_module_path( +//! process: Process, +//! name_ptr: *const u8, +//! name_len: usize, +//! buf_ptr: *mut u8, +//! buf_len_ptr: *mut usize, +//! ) -> bool; //! /// Stores the file system path of the executable in the buffer given. The //! /// path is a path that is accessible through the WASI file system, so a //! /// Windows path of `C:\foo\bar.exe` would be returned as