From 26a4f0a46bbaaa3e99ed206d1caa1d707c2816aa Mon Sep 17 00:00:00 2001 From: Andrew Werner Date: Tue, 1 Aug 2023 20:50:23 -0400 Subject: [PATCH] programs/uprobe: extract library path resolving The function is extracted so that a test could be written. This test is valid on linux-gnu targets, and it doesn't need any special privileges. This is anticipation of removing the code that uses this functionality (seemingly incidentally) from integration tests. --- aya/src/programs/uprobe.rs | 86 +++++++++++++++++++++++++------------- 1 file changed, 56 insertions(+), 30 deletions(-) diff --git a/aya/src/programs/uprobe.rs b/aya/src/programs/uprobe.rs index 891e9a07..b2cadf53 100644 --- a/aya/src/programs/uprobe.rs +++ b/aya/src/programs/uprobe.rs @@ -82,36 +82,7 @@ impl UProbe { target: T, pid: Option, ) -> Result { - let target = target.as_ref(); - let target_str = &*target.as_os_str().to_string_lossy(); - - let mut path = if let Some(pid) = pid { - find_lib_in_proc_maps(pid, target_str).map_err(|io_error| UProbeError::FileError { - filename: format!("/proc/{pid}/maps"), - io_error, - })? - } else { - None - }; - - if path.is_none() { - path = if target.is_absolute() { - Some(target_str) - } else { - let cache = - LD_SO_CACHE - .as_ref() - .map_err(|error| UProbeError::InvalidLdSoCache { - io_error: error.clone(), - })?; - cache.resolve(target_str) - } - .map(String::from) - }; - - let path = path.ok_or(UProbeError::InvalidTarget { - path: target.to_owned(), - })?; + let path = resolve_attach_path(target, pid)?; let sym_offset = if let Some(fn_name) = fn_name { resolve_symbol(&path, fn_name).map_err(|error| UProbeError::SymbolError { @@ -152,6 +123,61 @@ impl UProbe { } } +// Look up the path for the target. If it there is a pid, and the target is a library name +// that is in the process's memory map, use the path of that library. Otherwise, use the target as-is. +fn resolve_attach_path>( + target: T, + pid: Option, +) -> Result { + let target = target.as_ref(); + let target_str = &*target.as_os_str().to_string_lossy(); + + let mut path = if let Some(pid) = pid { + find_lib_in_proc_maps(pid, target_str).map_err(|io_error| UProbeError::FileError { + filename: format!("/proc/{pid}/maps"), + io_error, + })? + } else { + None + }; + + if path.is_none() { + path = if target.is_absolute() { + Some(target_str) + } else { + let cache = LD_SO_CACHE + .as_ref() + .map_err(|error| UProbeError::InvalidLdSoCache { + io_error: error.clone(), + })?; + cache.resolve(target_str) + } + .map(String::from) + }; + + path.ok_or(UProbeError::InvalidTarget { + path: target.to_owned(), + }) +} + +// Only run this test on linux with glibc because only in that configuration do we know that we'll +// be dynamically linked to libc and can exercise resolving the path to libc via the current +// process's memory map. +#[test] +#[cfg(all(target_os = "linux", target_env = "gnu"))] +#[cfg_attr(miri, ignore)] +fn test_resolve_attach_path() { + // Look up the current process's pid. + let pid = std::process::id(); + + // Now let's resolve the path to libc. It should exist in the current process's memory map and + // then in the ld.so.cache. + let libc_path = resolve_attach_path("libc", Some(pid.try_into().unwrap())).unwrap(); + + // Make sure we got a path that contains libc. + assert!(libc_path.contains("libc"), "libc_path: {}", libc_path); +} + define_link_wrapper!( /// The link used by [UProbe] programs. UProbeLink,