1use anyhow::Result;
4use std::collections::HashSet;
5use std::path::Path;
6use std::process::Command;
7
8use crate::paths::find_library;
9
10pub fn get_library_dependencies(binary_path: &Path) -> Result<Vec<String>> {
15 let output = Command::new("readelf")
16 .args(["-d"])
17 .arg(binary_path)
18 .output();
19
20 let output = match output {
21 Ok(o) => o,
22 Err(_) => return Ok(Vec::new()), };
24
25 if !output.status.success() {
26 return Ok(Vec::new());
28 }
29
30 let stdout = String::from_utf8_lossy(&output.stdout);
31 parse_readelf_output(&stdout)
32}
33
34pub fn parse_readelf_output(output: &str) -> Result<Vec<String>> {
44 let mut libs = Vec::new();
45
46 for line in output.lines() {
47 if line.contains("(NEEDED)") && line.contains("Shared library:") {
49 if let Some(start) = line.find('[') {
51 if let Some(end) = line.find(']') {
52 let lib_name = &line[start + 1..end];
53 libs.push(lib_name.to_string());
54 }
55 }
56 }
57 }
58
59 Ok(libs)
60}
61
62pub fn get_all_dependencies(
67 source_root: &Path,
68 binary_path: &Path,
69 extra_lib_paths: &[&str],
70) -> Result<HashSet<String>> {
71 let mut all_libs = HashSet::new();
72 let mut to_process = vec![binary_path.to_path_buf()];
73 let mut processed = HashSet::new();
74
75 while let Some(path) = to_process.pop() {
76 if processed.contains(&path) {
77 continue;
78 }
79 processed.insert(path.clone());
80
81 let deps = get_library_dependencies(&path)?;
82 for lib_name in deps {
83 if all_libs.insert(lib_name.clone()) {
84 if let Some(lib_path) = find_library(source_root, &lib_name, extra_lib_paths) {
86 to_process.push(lib_path);
87 }
88 }
89 }
90 }
91
92 Ok(all_libs)
93}
94
95#[cfg(test)]
96mod tests {
97 use super::*;
98
99 #[test]
100 fn test_parse_readelf_output() {
101 let output = r#"
102Dynamic section at offset 0x2d0e0 contains 28 entries:
103 Tag Type Name/Value
104 0x0000000000000001 (NEEDED) Shared library: [libtinfo.so.6]
105 0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
106 0x000000000000000c (INIT) 0x5000
107"#;
108 let libs = parse_readelf_output(output).unwrap();
109 assert_eq!(libs, vec!["libtinfo.so.6", "libc.so.6"]);
110 }
111
112 #[test]
113 fn test_parse_readelf_empty() {
114 let output = "not an ELF file";
115 let libs = parse_readelf_output(output).unwrap();
116 assert!(libs.is_empty());
117 }
118}