use crate::info::{get_elf_type, machine_from_e_machine, RlddRexInfo};
use crate::utils::{
build_search_dirs, empty_info, extra_lib_dirs_for_bin, find_library, open_and_map,
resolve_origin,
};
use crate::ElfArch;
use goblin::elf::Elf;
use std::collections::HashSet;
use std::fmt::Debug;
use std::fs;
use std::io;
use std::os::unix::fs::MetadataExt;
use std::path::{Path, PathBuf};
#[cfg(feature = "enable_ld_library_path")]
use crate::info::is_same_arch;
const MAX_DEPTH: usize = 512;
fn inner(
path: &Path,
elf: &Elf,
visited: &mut HashSet<(u64, u64)>,
seen_libs: &mut HashSet<String>,
res: &mut Vec<(String, String)>,
dirs: &[PathBuf],
arch: ElfArch,
d: usize,
) -> io::Result<()> {
if d > MAX_DEPTH {
eprintln!("Warning: max recursion depth at {:?}", path);
return Ok(());
}
if let Ok(meta) = fs::metadata(path) {
let key = (meta.dev(), meta.ino());
if !visited.insert(key) {
return Ok(());
}
} else {
eprintln!("Error access {:?}", path);
return Ok(());
}
let deps: Vec<_> = elf.libraries.iter().map(ToString::to_string).collect();
let paths: Vec<_> = elf
.rpaths
.iter()
.chain(&elf.runpaths)
.map(|s| resolve_origin(path, s))
.collect();
for dep in deps {
if !seen_libs.insert(dep.clone()) {
continue;
}
let display = find_library(&dep, dirs, &paths)
.map(|found| {
if let Ok(map) = open_and_map(&found) {
if let Ok(s_elf) = Elf::parse(&map) {
#[cfg(feature = "enable_ld_library_path")]
if !is_same_arch(arch, &s_elf) {
return "arch mismatch".into(); }
if let Err(e) =
inner(&found, &s_elf, visited, seen_libs, res, dirs, arch, d + 1)
{
eprintln!("Recursive error {:?}: {:?}", found, e);
}
}
}
found.display().to_string()
})
.unwrap_or_else(|| "not found".into());
res.push((dep, display));
}
Ok(())
}
pub fn rldd_minimal<P: AsRef<Path> + Debug>(path: P) -> io::Result<RlddRexInfo> {
let (mut libs, mut visited) = (HashSet::new(), HashSet::new());
let mut res = Vec::new();
let map = match open_and_map(&path) {
Ok(m) => m,
Err(e) => {
eprintln!("Fail to open or map {:?}: {}", path, e);
return Ok(empty_info());
}
};
let elf = match Elf::parse(&map) {
Ok(e) => e,
Err(e) => {
eprintln!("Fail to parser ELF {:?}: {}", path, e);
return Ok(empty_info());
}
};
let arch = [ElfArch::Elf32, ElfArch::Elf64][elf.is_64 as usize];
let machine = machine_from_e_machine(elf.header.e_machine);
let elf_type = get_elf_type(&elf);
if let Some(interp) = elf.interpreter {
if interp.contains("musl") {
let interp_path = PathBuf::from(interp);
let resolved_interp = if interp_path.exists() {
interp_path.canonicalize().unwrap_or(interp_path.clone())
} else {
interp_path.clone()
};
let lib_name = interp_path
.file_name()
.and_then(|name| name.to_str())
.unwrap_or(interp)
.to_string();
res.push((lib_name.clone(), resolved_interp.display().to_string()));
libs.insert(lib_name);
}
}
let mut search_dirs = build_search_dirs(&elf, arch, machine);
search_dirs.extend(extra_lib_dirs_for_bin(path.as_ref()));
inner(
path.as_ref(),
&elf,
&mut visited,
&mut libs,
&mut res,
&search_dirs,
arch,
0,
)?;
Ok(RlddRexInfo {
arch,
elf_type,
deps: res,
})
}