lddtree-0.3.7/.cargo_vcs_info.json0000644000000001360000000000100124660ustar { "git": { "sha1": "1dde220767eac80c15bdbb30fdf18948f1c9614d" }, "path_in_vcs": "" }lddtree-0.3.7/.github/workflows/CI.yml000064400000000000000000000022061046102023000156710ustar 00000000000000on: [push, pull_request] name: CI jobs: check: name: Check runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: stable override: true - uses: actions-rs/cargo@v1 with: command: check test: name: Test Suite runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest, macos-latest, windows-latest] steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: stable override: true - uses: actions-rs/cargo@v1 env: RUST_BACKTRACE: '1' with: command: test fmt: name: Rustfmt runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: stable override: true - run: rustup component add rustfmt - uses: actions-rs/cargo@v1 with: command: fmt args: --all -- --check lddtree-0.3.7/.gitignore000064400000000000000000000000101046102023000132350ustar 00000000000000/target lddtree-0.3.7/Cargo.lock0000644000000053620000000000100104470ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 4 [[package]] name = "autocfg" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "fs-err" version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8bb60e7409f34ef959985bc9d9c5ee8f5db24ee46ed9775850548021710f807f" dependencies = [ "autocfg", ] [[package]] name = "glob" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "goblin" version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53ab3f32d1d77146981dea5d6b1e8fe31eedcb7013e5e00d6ccd1259a4b4d923" dependencies = [ "log", "plain", "scroll", ] [[package]] name = "lddtree" version = "0.3.7" dependencies = [ "fs-err", "glob", "goblin", ] [[package]] name = "log" version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "plain" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" [[package]] name = "proc-macro2" version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] [[package]] name = "scroll" version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ab8598aa408498679922eff7fa985c25d58a90771bd6be794434c5277eab1a6" dependencies = [ "scroll_derive", ] [[package]] name = "scroll_derive" version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f81c2fde025af7e69b1d1420531c8a8811ca898919db177141a85313b1cb932" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "syn" version = "2.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "unicode-ident" version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" lddtree-0.3.7/Cargo.toml0000644000000022100000000000100104570ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" name = "lddtree" version = "0.3.7" build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "Read the ELF dependency tree" readme = "README.md" keywords = [ "ldd", "elf", ] license = "MIT" repository = "https://github.com/messense/lddtree-rs" [lib] name = "lddtree" path = "src/lib.rs" [[bin]] name = "lddtree" path = "src/main.rs" [[test]] name = "test_lddtree" path = "tests/test_lddtree.rs" [[test]] name = "test_parse_ldsoconf" path = "tests/test_parse_ldsoconf.rs" [dependencies.fs-err] version = "3.0.0" [dependencies.glob] version = "0.3.0" [dependencies.goblin] version = "0.9.0" lddtree-0.3.7/Cargo.toml.orig0000644000000006030000000000100114220ustar [package] name = "lddtree" version = "0.3.7" edition = "2021" description = "Read the ELF dependency tree" license = "MIT" keywords = ["ldd", "elf"] readme = "README.md" repository = "https://github.com/messense/lddtree-rs" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] fs-err = "3.0.0" glob = "0.3.0" goblin = "0.9.0" lddtree-0.3.7/Cargo.toml.orig000064400000000000000000000006031046102023000141440ustar 00000000000000[package] name = "lddtree" version = "0.3.7" edition = "2021" description = "Read the ELF dependency tree" license = "MIT" keywords = ["ldd", "elf"] readme = "README.md" repository = "https://github.com/messense/lddtree-rs" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] fs-err = "3.0.0" glob = "0.3.0" goblin = "0.9.0" lddtree-0.3.7/LICENSE000064400000000000000000000051671046102023000122740ustar 00000000000000MIT License Copyright (c) 2021 messense Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------- This software is based on auditwheel, provided under the following license: The MIT License Copyright (c) 2016 Robert T. McGibbon Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Some of the auditwheel's ELF-handling code was copied from gentoo's pax-utils/lddtree.py, available at https://sources.gentoo.org/cgi-bin/viewvc.cgi/gentoo-projects/pax-utils/lddtree.py Copyright 2012-2014 Gentoo Foundation Copyright 2012-2014 Mike Frysinger Copyright 2012-2014 The Chromium OS Authors Use of this source code is governed by a BSD-style license (BSD-3) lddtree-0.3.7/README.md000064400000000000000000000024251046102023000125400ustar 00000000000000# lddtree-rs [![CI](https://github.com/messense/lddtree-rs/workflows/CI/badge.svg)](https://github.com/messense/lddtree-rs/actions?query=workflow%3ACI) [![Crates.io](https://img.shields.io/crates/v/lddtree.svg)](https://crates.io/crates/lddtree) [![docs.rs](https://docs.rs/lddtree/badge.svg)](https://docs.rs/lddtree/) Read the ELF dependency tree, this does not work like `ldd` in that we do not execute/load code (only read files on disk). This is roughly a Rust port of the [lddtree.py](https://github.com/pypa/auditwheel/blob/main/src/auditwheel/lddtree.py) from the [auditwheel](https://github.com/pypa/auditwheel) project. It's intended to be used in [maturin](https://github.com/PyO3/maturin) for [implementing automatic repair of manylinux and musllinux wheels](https://github.com/PyO3/maturin/pull/742). ## Installation Add it to your ``Cargo.toml``: ```toml [dependencies] lddtree = "0.3" ``` ## Command line utility There is also a simple cli utility which can be installed via ```bash cargo install lddtree ``` Usage: `lddtree [root]` * `pathname` is the path to a linux shared library. * `root` is an optional path to a sysroot directory. ## License This work is released under the MIT license. A copy of the license is provided in the [LICENSE](./LICENSE) file. lddtree-0.3.7/src/errors.rs000064400000000000000000000020431046102023000137260ustar 00000000000000use std::error; use std::fmt; use std::io; use crate::ld_so_conf::LdSoConfError; #[derive(Debug)] pub enum Error { Io(io::Error), Goblin(goblin::error::Error), LdSoConf(LdSoConfError), } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Error::Io(e) => e.fmt(f), Error::Goblin(e) => e.fmt(f), Error::LdSoConf(e) => e.fmt(f), } } } impl error::Error for Error { fn source(&self) -> Option<&(dyn error::Error + 'static)> { match self { Error::Io(e) => e.source(), Error::Goblin(e) => e.source(), Error::LdSoConf(e) => e.source(), } } } impl From for Error { fn from(e: io::Error) -> Self { Error::Io(e) } } impl From for Error { fn from(e: goblin::error::Error) -> Self { Error::Goblin(e) } } impl From for Error { fn from(e: LdSoConfError) -> Self { Error::LdSoConf(e) } } lddtree-0.3.7/src/ld_so_conf.rs000064400000000000000000000047761046102023000145360ustar 00000000000000use std::error::Error; use std::fmt; use std::io; use std::path::Path; use fs_err as fs; #[derive(Debug)] pub enum LdSoConfError { /// I/O error Io(io::Error), /// Glob iteration error Glob(glob::GlobError), /// Invalid include directive InvalidIncludeDirective(String), } impl fmt::Display for LdSoConfError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { LdSoConfError::Io(e) => e.fmt(f), LdSoConfError::Glob(e) => e.fmt(f), LdSoConfError::InvalidIncludeDirective(line) => { write!(f, "invalid include directive: {}", line) } } } } impl Error for LdSoConfError { fn source(&self) -> Option<&(dyn Error + 'static)> { match self { LdSoConfError::Io(e) => e.source(), LdSoConfError::Glob(e) => e.source(), LdSoConfError::InvalidIncludeDirective(_) => None, } } } impl From for LdSoConfError { fn from(e: io::Error) -> Self { LdSoConfError::Io(e) } } impl From for LdSoConfError { fn from(e: glob::GlobError) -> Self { LdSoConfError::Glob(e) } } /// Parse the `ld.so.conf` file on Linux pub fn parse_ld_so_conf( path: impl AsRef, root: impl AsRef, ) -> Result, LdSoConfError> { let path = path.as_ref(); let root = root.as_ref(); let conf = fs::read_to_string(path)?; let mut paths = Vec::new(); for line in conf.lines() { if line.starts_with("#") { continue; } if line.starts_with("include ") { let include_path = &line[8..]; let include_path = if !include_path.starts_with('/') { let parent = path.parent().unwrap(); parent.join(include_path).display().to_string() } else { root.join(include_path.strip_prefix('/').unwrap_or(include_path)) .display() .to_string() }; for path in glob::glob(&include_path).map_err(|err| { LdSoConfError::InvalidIncludeDirective(format!("{} in '{}'", err, line)) })? { let path = path?; paths.extend(parse_ld_so_conf(&path, root)?); } } else { paths.push( root.join(line.strip_prefix('/').unwrap_or(line)) .display() .to_string(), ); } } Ok(paths) } lddtree-0.3.7/src/lib.rs000064400000000000000000000274321046102023000131710ustar 00000000000000//! Read the ELF dependency tree. //! //! This does not work like `ldd` in that we do not execute/load code (only read //! files on disk). use std::collections::HashMap; use std::env; use std::path::{Path, PathBuf}; use fs_err as fs; use goblin::elf::{ header::{EI_OSABI, ELFOSABI_GNU, ELFOSABI_NONE}, Elf, }; mod errors; pub mod ld_so_conf; pub use errors::Error; use ld_so_conf::parse_ld_so_conf; /// A library dependency #[derive(Debug, Clone)] pub struct Library { /// Library name pub name: String, /// The path to the library. pub path: PathBuf, /// The normalized real path to the library. pub realpath: Option, /// The dependencies of this library. pub needed: Vec, /// Runtime library search paths. (deprecated) pub rpath: Vec, /// Runtime library search paths. pub runpath: Vec, } impl Library { /// Is this library found in filesystem. pub fn found(&self) -> bool { self.realpath.is_some() } } /// Library dependency tree #[derive(Debug, Clone)] pub struct DependencyTree { /// The binary’s program interpreter (e.g., dynamic linker). pub interpreter: Option, /// A list of this binary’s dynamic libraries it depends on directly. pub needed: Vec, /// All of this binary’s dynamic libraries it uses in detail. pub libraries: HashMap, /// Runtime library search paths. (deprecated) pub rpath: Vec, /// Runtime library search paths. pub runpath: Vec, } /// Library dependency analyzer #[derive(Debug, Clone)] pub struct DependencyAnalyzer { env_ld_paths: Vec, conf_ld_paths: Vec, additional_ld_paths: Vec, runpaths: Vec, root: PathBuf, } impl Default for DependencyAnalyzer { fn default() -> Self { Self::new(PathBuf::from("/")) } } impl DependencyAnalyzer { /// Create a new dependency analyzer. pub fn new(root: PathBuf) -> DependencyAnalyzer { DependencyAnalyzer { env_ld_paths: Vec::new(), conf_ld_paths: Vec::new(), additional_ld_paths: Vec::new(), runpaths: Vec::new(), root, } } /// Add additional library path /// /// Additional library paths are treated as absolute paths, /// not relative to `root` pub fn add_library_path(mut self, path: PathBuf) -> Self { self.additional_ld_paths.push(path); self } /// Set additional library paths /// /// Additional library paths are treated as absolute paths, /// not relative to `root` pub fn library_paths(mut self, paths: Vec) -> Self { self.additional_ld_paths = paths; self } fn read_rpath_runpath( &self, elf: &Elf, path: &Path, ) -> Result<(Vec, Vec), Error> { let mut rpaths = Vec::new(); let mut runpaths = Vec::new(); for runpath in &elf.runpaths { if let Ok(ld_paths) = self.parse_ld_paths(runpath, path) { runpaths = ld_paths; } } for rpath in &elf.rpaths { if let Ok(ld_paths) = self.parse_ld_paths(rpath, path) { rpaths = ld_paths; } } Ok((rpaths, runpaths)) } /// Analyze the given binary. pub fn analyze(mut self, path: impl AsRef) -> Result { let path = path.as_ref(); self.load_ld_paths(path)?; let bytes = fs::read(path)?; let elf = Elf::parse(&bytes)?; let (mut rpaths, runpaths) = self.read_rpath_runpath(&elf, path)?; if !runpaths.is_empty() { // If both RPATH and RUNPATH are set, only the latter is used. rpaths = Vec::new(); } self.runpaths = runpaths.clone(); self.runpaths.extend(rpaths.clone()); let needed: Vec = elf.libraries.iter().map(ToString::to_string).collect(); let mut libraries = HashMap::new(); let mut stack = needed.clone(); while let Some(lib_name) = stack.pop() { if libraries.contains_key(&lib_name) { continue; } let library = self.find_library(&elf, &lib_name)?; libraries.insert(lib_name, library.clone()); stack.extend(library.needed); } let interpreter = elf.interpreter.map(|interp| interp.to_string()); if let Some(ref interp) = interpreter { if !libraries.contains_key(interp) { let interp_path = self.root.join(interp.strip_prefix('/').unwrap_or(interp)); let interp_name = interp_path .file_name() .expect("missing filename") .to_str() .expect("Filename isn't valid Unicode"); let interp_realpath = fs::canonicalize(PathBuf::from(&interp_path)).ok(); libraries.insert( interp.to_string(), Library { name: interp_name.to_string(), path: interp_path, realpath: interp_realpath, needed: Vec::new(), rpath: Vec::new(), runpath: Vec::new(), }, ); } } let dep_tree = DependencyTree { interpreter, needed, libraries, rpath: rpaths, runpath: runpaths, }; Ok(dep_tree) } /// Parse the colon-delimited list of paths and apply ldso rules fn parse_ld_paths(&self, ld_path: &str, elf_path: &Path) -> Result, Error> { let mut paths = Vec::new(); for path in ld_path.split(':') { let normpath = if path.is_empty() { // The ldso treats empty paths as the current directory env::current_dir() } else if path.contains("$ORIGIN") || path.contains("${ORIGIN}") { let elf_path = fs::canonicalize(elf_path)?; let elf_dir = elf_path.parent().expect("no parent"); let replacement = elf_dir.to_str().unwrap(); let path = path .replace("${ORIGIN}", replacement) .replace("$ORIGIN", replacement); fs::canonicalize(PathBuf::from(path)) } else { fs::canonicalize(self.root.join(path.strip_prefix('/').unwrap_or(path))) }; if let Ok(normpath) = normpath { paths.push(normpath.display().to_string()); } } Ok(paths) } fn load_ld_paths(&mut self, elf_path: &Path) -> Result<(), Error> { #[cfg(unix)] if let Ok(env_ld_path) = env::var("LD_LIBRARY_PATH") { if self.root == Path::new("/") { self.env_ld_paths = self.parse_ld_paths(&env_ld_path, elf_path)?; } } // Load all the paths from a ldso config file match find_musl_libc() { // musl libc Ok(Some(_musl_libc)) => { // from https://git.musl-libc.org/cgit/musl/tree/ldso/dynlink.c?id=3f701faace7addc75d16dea8a6cd769fa5b3f260#n1063 let root_str = self.root.display().to_string(); let root_str = root_str.strip_suffix("/").unwrap_or(&root_str); let pattern = format!("{}/etc/ld-musl-*.path", root_str); for entry in glob::glob(&pattern).expect("invalid glob pattern") { if let Ok(entry) = entry { let content = fs::read_to_string(&entry)?; for line in content.lines() { let line_stripped = line.trim(); if !line_stripped.is_empty() { self.conf_ld_paths .push(root_str.to_string() + line_stripped); } } break; } } // default ld paths if self.conf_ld_paths.is_empty() { self.conf_ld_paths.push(root_str.to_string() + "/lib"); self.conf_ld_paths .push(root_str.to_string() + "/usr/local/lib"); self.conf_ld_paths.push(root_str.to_string() + "/usr/lib"); } } // glibc _ => { // Load up /etc/ld.so.conf if let Ok(paths) = parse_ld_so_conf("/etc/ld.so.conf", &self.root) { self.conf_ld_paths = paths; } // the trusted directories are not necessarily in ld.so.conf for path in &["/lib", "/lib64/", "/usr/lib", "/usr/lib64"] { self.conf_ld_paths.push(path.to_string()); } } } self.conf_ld_paths.dedup(); Ok(()) } /// Try to locate a `lib` that is compatible to `elf` fn find_library(&self, elf: &Elf, lib: &str) -> Result { for lib_path in self .runpaths .iter() .chain(self.env_ld_paths.iter()) .chain(self.conf_ld_paths.iter()) .map(|ld_path| { self.root .join(ld_path.strip_prefix('/').unwrap_or(ld_path)) .join(lib) }) .chain( self.additional_ld_paths .iter() .map(|ld_path| ld_path.join(lib)), ) { // FIXME: readlink to get real path if lib_path.exists() { let bytes = fs::read(&lib_path)?; if let Ok(lib_elf) = Elf::parse(&bytes) { if compatible_elfs(elf, &lib_elf) { let needed = lib_elf.libraries.iter().map(ToString::to_string).collect(); let (rpath, runpath) = self.read_rpath_runpath(&lib_elf, &lib_path)?; return Ok(Library { name: lib.to_string(), path: lib_path.to_path_buf(), realpath: fs::canonicalize(lib_path).ok(), needed, rpath, runpath, }); } } } } Ok(Library { name: lib.to_string(), path: PathBuf::from(lib), realpath: None, needed: Vec::new(), rpath: Vec::new(), runpath: Vec::new(), }) } } /// Find musl libc path fn find_musl_libc() -> Result, Error> { match glob::glob("/lib/libc.musl-*.so.1") .expect("invalid glob pattern") .next() { Some(Ok(path)) => Ok(Some(path)), _ => Ok(None), } } /// See if two ELFs are compatible /// /// This compares the aspects of the ELF to see if they're compatible: /// bit size, endianness, machine type, and operating system. fn compatible_elfs(elf1: &Elf, elf2: &Elf) -> bool { if elf1.is_64 != elf2.is_64 { return false; } if elf1.little_endian != elf2.little_endian { return false; } if elf1.header.e_machine != elf2.header.e_machine { return false; } let compatible_osabis = &[ ELFOSABI_NONE, // ELFOSABI_NONE / ELFOSABI_SYSV ELFOSABI_GNU, // ELFOSABI_GNU / ELFOSABI_LINUX ]; let osabi1 = elf1.header.e_ident[EI_OSABI]; let osabi2 = elf2.header.e_ident[EI_OSABI]; if osabi1 != osabi2 && !compatible_osabis.contains(&osabi1) && !compatible_osabis.contains(&osabi2) { return false; } true } lddtree-0.3.7/src/main.rs000064400000000000000000000041031046102023000133350ustar 00000000000000use std::collections::{HashMap, HashSet}; use std::env; use std::error::Error; use std::path::PathBuf; use std::process; use lddtree::{DependencyAnalyzer, Library}; fn main() -> Result<(), Box> { let mut args = env::args().skip(1); if let Some(pathname) = args.next() { let root = args .next() .map(|s| PathBuf::from(&s)) .unwrap_or_else(|| PathBuf::from("/")); let lib_paths = args.map(|s| PathBuf::from(&s)).collect(); let analyzer = DependencyAnalyzer::new(root).library_paths(lib_paths); let deps = analyzer.analyze(pathname)?; if let Some(interp) = deps.interpreter { if let Some(path) = deps .libraries .get(&interp) .and_then(|lib| lib.realpath.as_ref()) { println!("{} => {}", interp, path.display()); } else { println!("{} => not found", interp); } } for needed in deps.needed { print_library(&needed, &deps.libraries, 0, HashSet::new()); } } else { eprintln!("USAGE: lddtree [root] [library path...]"); process::exit(1); } Ok(()) } fn print_library( name: &str, libraries: &HashMap, level: usize, mut history: HashSet, ) { let padding = " ".repeat(level); if let Some(lib) = libraries.get(name) { if let Some(path) = lib.realpath.as_ref() { let looping = !history.insert(path.to_path_buf()); let loop_annotation = if looping { " (DEPENDENCY CYCLE)" } else { "" }; println!( "{}{} => {}{}", padding, name, path.display(), loop_annotation ); if looping { return; }; } else { println!("{}{} => not found", padding, name); } for needed in &lib.needed { print_library(needed, libraries, level + 4, history.clone()); } } } lddtree-0.3.7/tests/ld.so.conf000064400000000000000000000000341046102023000143030ustar 00000000000000include ld.so.conf.d/*.conf lddtree-0.3.7/tests/ld.so.conf.d/fakeroot-x86_64-linux-gnu.conf000064400000000000000000000000461046102023000221460ustar 00000000000000/usr/lib/x86_64-linux-gnu/libfakeroot lddtree-0.3.7/tests/ld.so.conf.d/libc.conf000064400000000000000000000000541046102023000163640ustar 00000000000000# libc default configuration /usr/local/lib lddtree-0.3.7/tests/ld.so.conf.d/x86_64-linux-gnu.conf000064400000000000000000000001441046102023000203350ustar 00000000000000# Multiarch support /usr/local/lib/x86_64-linux-gnu /lib/x86_64-linux-gnu /usr/lib/x86_64-linux-gnu lddtree-0.3.7/tests/ld.so.conf.d/zz_i386-biarch-compat.conf000064400000000000000000000000701046102023000213740ustar 00000000000000# Legacy biarch compatibility support /lib32 /usr/lib32 lddtree-0.3.7/tests/test_lddtree.rs000064400000000000000000000010031046102023000154420ustar 00000000000000use lddtree::DependencyAnalyzer; #[test] fn test_lddtree() { let analyzer = DependencyAnalyzer::default(); let deps = analyzer.analyze("tests/test.elf").unwrap(); assert_eq!( deps.interpreter.as_deref(), Some("/lib/ld-linux-aarch64.so.1") ); assert_eq!( deps.needed, &[ "libz.so.1", "libpthread.so.0", "libm.so.6", "libdl.so.2", "libc.so.6", ] ); assert_eq!(deps.libraries.len(), 6); } lddtree-0.3.7/tests/test_parse_ldsoconf.rs000064400000000000000000000007231046102023000170300ustar 00000000000000use lddtree::ld_so_conf::parse_ld_so_conf; #[test] fn test_parse_ldsoconf() { let paths = parse_ld_so_conf("tests/ld.so.conf", "/").unwrap(); assert_eq!( paths, vec![ "/usr/lib/x86_64-linux-gnu/libfakeroot", "/usr/local/lib", "/usr/local/lib/x86_64-linux-gnu", "/lib/x86_64-linux-gnu", "/usr/lib/x86_64-linux-gnu", "/lib32", "/usr/lib32", ] ); }