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 in anticipation of removing the code that uses this functionality
(seemingly incidentally) from integration tests.
reviewable/pr717/r9
Andrew Werner 1 year ago
parent 368ddf10c4
commit dcc6b84a88

@ -82,36 +82,7 @@ impl UProbe {
target: T, target: T,
pid: Option<pid_t>, pid: Option<pid_t>,
) -> Result<UProbeLinkId, ProgramError> { ) -> Result<UProbeLinkId, ProgramError> {
let target = target.as_ref(); let path = resolve_attach_path(target, pid)?;
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 sym_offset = if let Some(fn_name) = fn_name { let sym_offset = if let Some(fn_name) = fn_name {
resolve_symbol(&path, fn_name).map_err(|error| UProbeError::SymbolError { resolve_symbol(&path, fn_name).map_err(|error| UProbeError::SymbolError {
@ -152,6 +123,64 @@ impl UProbe {
} }
} }
fn resolve_attach_path<T: AsRef<Path>>(
target: T,
pid: Option<pid_t>,
) -> Result<String, UProbeError> {
// 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.
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_attr(
any(miri, not(all(target_os = "linux", target_env = "gnu"))),
ignore = "requires glibc, doesn't work in miri"
)]
fn test_resolve_attach_path() {
// Look up the current process's pid.
let pid = std::process::id().try_into().unwrap();
// 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)).unwrap();
// Make sure we got a path that contains libc.
assert!(libc_path.contains("libc"), "libc_path: {}", libc_path);
}
define_link_wrapper!( define_link_wrapper!(
/// The link used by [UProbe] programs. /// The link used by [UProbe] programs.
UProbeLink, UProbeLink,

Loading…
Cancel
Save