forked from matteo/sysroot-cleaner
fix(dso): allow specifying extra lib folders on the cmdline
Before we were relying on LD_LIBRARY_PATH, but it is misleading as it will most likely also affect the sysroot-cleaner binary itself. This instead introduces a separate command line argument, `--ld-path`, to specify a list of paths to search. Also includes some minor refactoring.
This commit is contained in:
parent
6d06377154
commit
1922297b4a
5 changed files with 112 additions and 95 deletions
18
src/args.rs
18
src/args.rs
|
@ -4,8 +4,8 @@
|
|||
use std::path::PathBuf;
|
||||
|
||||
use clap::{
|
||||
Parser,
|
||||
builder::{PathBufValueParser, TypedValueParser},
|
||||
Parser,
|
||||
};
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
|
@ -59,4 +59,20 @@ pub struct Args {
|
|||
|
||||
/// The location of the sysroot to clean up
|
||||
pub sysroot_location: PathBuf,
|
||||
|
||||
/// An additional path to consider when resolving
|
||||
/// libraries, relative to the sysroot.
|
||||
/// Their behavior is similar of the one of the
|
||||
/// `LD_LIBRARY_PATH` environment variable when
|
||||
/// specified to the dynamic linker.
|
||||
#[arg(long = "ld-path", value_parser = relativize_path)]
|
||||
pub extra_library_paths: Vec<PathBuf>,
|
||||
}
|
||||
|
||||
fn relativize_path(arg: &str) -> anyhow::Result<PathBuf> {
|
||||
let mut p = PathBuf::from(arg);
|
||||
if p.is_absolute() {
|
||||
p = p.strip_prefix("/")?.into();
|
||||
}
|
||||
Ok(p)
|
||||
}
|
||||
|
|
|
@ -64,7 +64,10 @@ impl Runner {
|
|||
cleaners.push(Box::new(ListCleaner::new(list::ListType::Block, bl)));
|
||||
}
|
||||
|
||||
cleaners.push(Box::new(DsoCleaner::new(args.output_dotfile)));
|
||||
cleaners.push(Box::new(DsoCleaner::new(
|
||||
args.extra_library_paths,
|
||||
args.output_dotfile,
|
||||
)));
|
||||
|
||||
Self {
|
||||
cleaners,
|
||||
|
|
|
@ -27,17 +27,22 @@ type InodeGraph = DiGraphMap<ino_t, ()>;
|
|||
/// Cleans up unused shared libraries
|
||||
/// and warns about broken dependencies as well
|
||||
pub struct DsoCleaner {
|
||||
extra_library_paths: Vec<PathBuf>,
|
||||
output_dot: Option<PathBuf>,
|
||||
}
|
||||
|
||||
struct State {
|
||||
ld_library_path: Vec<String>,
|
||||
paths_map: InodeMap,
|
||||
graph: InodeGraph,
|
||||
}
|
||||
|
||||
impl DsoCleaner {
|
||||
pub fn new(output_dot: Option<PathBuf>) -> Self {
|
||||
Self { output_dot }
|
||||
pub fn new(extra_library_paths: Vec<PathBuf>, output_dot: Option<PathBuf>) -> Self {
|
||||
Self {
|
||||
extra_library_paths,
|
||||
output_dot,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -52,6 +57,11 @@ impl Cleaner for DsoCleaner {
|
|||
output: mpsc::Sender<Decision>,
|
||||
) -> Result<()> {
|
||||
let mut state = State::default();
|
||||
state.ld_library_path = self
|
||||
.extra_library_paths
|
||||
.iter()
|
||||
.map(|p| p.to_str().unwrap().to_owned())
|
||||
.collect();
|
||||
|
||||
let mut inodes_to_keep = HashSet::new();
|
||||
inodes_to_keep.insert(ROOT_NODE);
|
||||
|
@ -127,7 +137,11 @@ impl Default for State {
|
|||
paths_map.insert(ROOT_NODE, HashSet::from([fake_root_node]));
|
||||
graph.add_node(ROOT_NODE);
|
||||
|
||||
Self { paths_map, graph }
|
||||
Self {
|
||||
ld_library_path: vec![],
|
||||
paths_map,
|
||||
graph,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -196,7 +210,7 @@ impl State {
|
|||
self.update_graph("".into(), ROOT_NODE, path.to_owned(), src.st_ino);
|
||||
}
|
||||
|
||||
let search_paths = determine_lib_search_paths(path, elf)?;
|
||||
let search_paths = self.determine_lib_search_paths(path, elf)?;
|
||||
log::trace!("determined search paths {:#?}", search_paths);
|
||||
|
||||
'next_lib: for &library in elf.libraries.iter() {
|
||||
|
@ -292,61 +306,45 @@ impl State {
|
|||
)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn determine_lib_search_paths(path: &Path, elf: &Elf<'_>) -> Result<Vec<String>> {
|
||||
log::trace!("elf.runpaths = {:#?}", elf.runpaths);
|
||||
log::trace!("elf.rpaths = {:#?}", elf.rpaths);
|
||||
// Contract: only relative paths starting from the sysroot dir should be returned from this function
|
||||
fn determine_lib_search_paths(&self, path: &Path, elf: &Elf<'_>) -> Result<Vec<String>> {
|
||||
log::trace!(
|
||||
"{}: elf.runpaths = {:#?}, elf.rpaths = {:#?}",
|
||||
path.display(),
|
||||
elf.runpaths,
|
||||
elf.rpaths
|
||||
);
|
||||
|
||||
let mut search_paths = vec![];
|
||||
let current_dir = std::env::current_dir()?;
|
||||
let origin = fs::canonicalize(path)?
|
||||
.parent()
|
||||
.unwrap()
|
||||
.strip_prefix(current_dir)?
|
||||
.to_path_buf()
|
||||
.into_os_string()
|
||||
.into_string()
|
||||
.map_err(|s| anyhow::anyhow!("cannot represent {:?} as a UTF-8 string", s))?;
|
||||
|
||||
let current_dir = std::env::current_dir()?;
|
||||
let origin = fs::canonicalize(path)?
|
||||
.parent()
|
||||
.unwrap()
|
||||
.strip_prefix(current_dir)?
|
||||
.to_path_buf()
|
||||
.into_os_string()
|
||||
.into_string()
|
||||
.map_err(|s| anyhow::anyhow!("cannot represent {:?} as a UTF-8 string", s))?;
|
||||
let mut search_paths = vec![];
|
||||
if elf.runpaths.is_empty() {
|
||||
search_paths.extend(collect_paths(&elf.rpaths, &origin));
|
||||
}
|
||||
|
||||
if elf.runpaths.is_empty() {
|
||||
collect_paths(&elf.rpaths, &mut search_paths, &origin);
|
||||
}
|
||||
|
||||
search_paths.append(&mut get_env_library_paths());
|
||||
|
||||
collect_paths(&elf.runpaths, &mut search_paths, &origin);
|
||||
|
||||
search_paths.push("usr/local/lib".into());
|
||||
search_paths.push("lib".into());
|
||||
search_paths.push("usr/lib".into());
|
||||
Ok(search_paths)
|
||||
}
|
||||
|
||||
fn collect_paths(elf_paths: &Vec<&str>, search_paths: &mut Vec<String>, origin: &str) {
|
||||
if elf_paths.iter().any(|p| !p.is_empty()) {
|
||||
let mut paths = elf_paths
|
||||
.iter()
|
||||
.flat_map(|p| p.split(':'))
|
||||
.map(|p| p.replace("$ORIGIN", origin))
|
||||
.map(|p| p.trim_start_matches('/').to_string())
|
||||
.collect::<Vec<_>>();
|
||||
search_paths.append(&mut paths);
|
||||
search_paths.extend(self.ld_library_path.clone());
|
||||
search_paths.extend(collect_paths(&elf.runpaths, &origin));
|
||||
search_paths.extend(["usr/local/lib".into(), "lib".into(), "usr/lib".into()]);
|
||||
Ok(search_paths)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_env_library_paths() -> Vec<String> {
|
||||
let ld_config_path = std::env::var("LD_LIBRARY_PATH");
|
||||
ld_config_path
|
||||
.as_ref()
|
||||
.map(|env| {
|
||||
env.split(':')
|
||||
.filter(|s| s.is_empty())
|
||||
.map(|s| s.into())
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_default()
|
||||
fn collect_paths(elf_paths: &Vec<&str>, origin: &str) -> impl Iterator<Item = String> {
|
||||
elf_paths
|
||||
.iter()
|
||||
.flat_map(|&p| p.split(':')) // Split multiple elements in r(un)?path separated by ':'
|
||||
.filter(|&p| !p.is_empty()) // ignore empty items
|
||||
.map(|p| p.replace("$ORIGIN", origin)) // replace $ORIGIN with path rel to sysroot
|
||||
.map(|p| p.trim_start_matches('/').to_string()) // relativize paths from sysroot
|
||||
}
|
||||
|
||||
fn is_elf(f: &mut File) -> Result<bool> {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue