Strategies for finding binary dependencies
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

clean up into find_needed_libs

+228 -162
+140
src/bin/find_needed_libs.rs
··· 1 + // © Vlad-Stefan Harbuz <vlad@vlad.website> 2 + // SPDX-License-Identifier: Apache-2.0 3 + 4 + use std::collections::HashMap; 5 + use std::fs::File; 6 + use std::io::{self, BufRead}; 7 + 8 + use clap::Parser; 9 + use elf::ElfStream; 10 + use elf::endian::AnyEndian; 11 + use walkdir::WalkDir; 12 + 13 + /// Examine shared objects within a directory containing many extracted Python wheel files 14 + #[derive(Parser, Debug)] 15 + #[command(version, about, long_about = None)] 16 + struct Args { 17 + /// Target directory 18 + #[arg(short, long)] 19 + dir: String, 20 + } 21 + 22 + fn read_lines(filename: &str) -> io::Result<io::Lines<io::BufReader<File>>> { 23 + let file = File::open(filename)?; 24 + Ok(io::BufReader::new(file).lines()) 25 + } 26 + 27 + fn get_needed_libs( 28 + elf_file: &mut ElfStream<AnyEndian, std::fs::File>, 29 + ) -> Option<impl Iterator<Item = &str>> { 30 + let dyns = elf_file.dynamic().expect("failed to get .dynamic")?; 31 + let needed_dt_vals: Vec<u64> = dyns 32 + .iter() 33 + .filter(|d| d.d_tag == elf::abi::DT_NEEDED) 34 + .map(|d| d.d_val()) 35 + .collect(); 36 + 37 + let (_dynsyms, dynstrs) = elf_file 38 + .dynamic_symbol_table() 39 + .expect("failed to get .dynsym and string table")?; 40 + 41 + Some(needed_dt_vals.into_iter().map(move |dt_val| { 42 + dynstrs 43 + .get(dt_val as usize) 44 + .expect("failed to get name from string table") 45 + })) 46 + } 47 + 48 + fn get_wheel_name(wheel_dir_path: &str) -> Option<String> { 49 + for dist_info_candidate in WalkDir::new(wheel_dir_path).min_depth(1).max_depth(1) { 50 + let dist_info_candidate = dist_info_candidate.expect("could not read file"); 51 + let dist_info_candidate_path = dist_info_candidate 52 + .path() 53 + .to_str() 54 + .expect("could not convert path to str"); 55 + if dist_info_candidate_path.ends_with(".dist-info") { 56 + let metadata_path = format!("{}/METADATA", dist_info_candidate_path); 57 + let metadata_lines = read_lines(&metadata_path).expect("could not read METADATA"); 58 + for line in metadata_lines.map_while(Result::ok) { 59 + if line.starts_with("Name: ") { 60 + return Some(line.replace("Name: ", "")); 61 + } 62 + } 63 + } 64 + } 65 + None 66 + } 67 + 68 + fn main() { 69 + let args = Args::parse(); 70 + 71 + let mut found_libs = HashMap::new(); 72 + let mut n_so_files = 0; 73 + let mut n_bad_so_files = 0; 74 + let mut n_needed_libs = 0; 75 + 76 + for wheel_dir in WalkDir::new(args.dir).min_depth(1).max_depth(1) { 77 + let wheel_dir = wheel_dir.expect("could not read file"); 78 + let wheel_dir_meta = wheel_dir.metadata().expect("could not get file metadata"); 79 + if wheel_dir_meta.is_file() { 80 + continue; 81 + } 82 + let wheel_dir_path = wheel_dir 83 + .path() 84 + .to_str() 85 + .expect("could not convert path to str"); 86 + 87 + let name = get_wheel_name(wheel_dir_path); 88 + 89 + if let Some(name) = name { 90 + // println!("→ {}", name); 91 + 92 + for file in WalkDir::new(wheel_dir_path) { 93 + let file = file.expect("could not read file"); 94 + let filename = file 95 + .file_name() 96 + .to_str() 97 + .expect("could not covert filename to str"); 98 + let path = file.path().to_str().expect("could not convert path to str"); 99 + let is_so = path.ends_with(".so") || path.contains(".so."); 100 + if is_so { 101 + // println!("{}", filename); 102 + n_so_files += 1; 103 + let pathbuf = std::path::PathBuf::from(path); 104 + let file_stream = File::open(pathbuf).expect("could not open file"); 105 + let elf_file = ElfStream::<AnyEndian, _>::open_stream(file_stream); 106 + if let Ok(mut elf_file) = elf_file { 107 + let needed_libs = get_needed_libs(&mut elf_file); 108 + if let Some(needed_libs) = needed_libs { 109 + for name in needed_libs { 110 + n_needed_libs += 1; 111 + let libname = 112 + name.split(".so").next().expect("could not get libname"); 113 + // println!("\t{}", libname); 114 + found_libs 115 + .entry(libname.to_owned()) 116 + .and_modify(|entry| *entry += 1) 117 + .or_insert(1); 118 + } 119 + } 120 + } else { 121 + eprintln!("Could not open ELF stream for {}", path); 122 + n_bad_so_files += 1; 123 + } 124 + } 125 + } 126 + } else { 127 + panic!("Could not get name for {}", wheel_dir_path); 128 + } 129 + } 130 + 131 + let mut found_libs_vec: Vec<(&String, &u32)> = found_libs.iter().collect(); 132 + found_libs_vec.sort_by(|a, b| b.1.cmp(a.1)); 133 + for (name, count) in found_libs_vec { 134 + println!("{},{}", name, count); 135 + } 136 + 137 + eprintln!("Found {} .so files", n_so_files); 138 + eprintln!("Found {} bad .so files", n_bad_so_files); 139 + eprintln!("Found {} needed libs", n_needed_libs); 140 + }
-162
src/bin/print_symbols.rs
··· 1 - // © Vlad-Stefan Harbuz <vlad@vlad.website> 2 - // SPDX-License-Identifier: Apache-2.0 3 - 4 - // use std::ffi::OsString; 5 - // use std::path::Path; 6 - use std::fs::File; 7 - use std::io::{self, BufRead}; 8 - 9 - // use glob::glob; 10 - use clap::Parser; 11 - use comfy_table::{Cell, Table}; 12 - use elf::ElfStream; 13 - use elf::endian::AnyEndian; 14 - use walkdir::WalkDir; 15 - 16 - /// Examine shared objects within a directory containing many extracted Python wheel files 17 - #[derive(Parser, Debug)] 18 - #[command(version, about, long_about = None)] 19 - struct Args { 20 - /// Target directory 21 - #[arg(short, long)] 22 - dir: String, 23 - } 24 - 25 - fn read_lines(filename: &str) -> io::Result<io::Lines<io::BufReader<File>>> { 26 - let file = File::open(filename)?; 27 - Ok(io::BufReader::new(file).lines()) 28 - } 29 - 30 - /* 31 - From https://github.com/cole14/rust-readelf/blob/419f78ac79d2e5b538ffe28777b54a487d953840/src/rust-readelf.rs 32 - SPDX-SnippetBegin 33 - SPDX-License-Identifier: MIT 34 - SPDX-SnippetCopyrightText: Christopher Cole 35 - */ 36 - fn print_dynamic_symbol_table(elf_file: &mut ElfStream<AnyEndian, File>) { 37 - // Get the .dynsym table. If this file doesn't have one, then we're done 38 - let (dynsyms, dynstrs) = match elf_file 39 - .dynamic_symbol_table() 40 - .expect("Failed to get .dynsym and string table") 41 - { 42 - Some(tables) => tables, 43 - None => { 44 - return; 45 - } 46 - }; 47 - 48 - // Parse out all the symbols so that we can look up versions for them if needed. 49 - let symbols: Vec<(String, elf::symbol::Symbol)> = dynsyms 50 - .iter() 51 - .map(|sym| { 52 - ( 53 - dynstrs 54 - .get(sym.st_name as usize) 55 - .expect("Failed to get symbol name") 56 - .to_string(), 57 - sym, 58 - ) 59 - }) 60 - .collect(); 61 - 62 - let vertab = elf_file 63 - .symbol_version_table() 64 - .expect("Failed to parse GNU symbol versions"); 65 - 66 - let mut table = Table::new(); 67 - table.set_header([ 68 - "ndx", 69 - "value", 70 - "size", 71 - "type", 72 - "bind", 73 - "visibility", 74 - "shndx", 75 - "needs version", 76 - "name", 77 - ]); 78 - for (sym_idx, (sym_name, sym)) in symbols.iter().enumerate() { 79 - let needs_name = match &vertab { 80 - Some(vertab) => { 81 - if sym.is_undefined() { 82 - match vertab 83 - .get_requirement(sym_idx) 84 - .expect("Failed to parse symbol requirement") 85 - { 86 - Some(req) => req.name, 87 - None => "None", 88 - } 89 - } else { 90 - "None" 91 - } 92 - } 93 - None => "None", 94 - }; 95 - let cells: Vec<Cell> = vec![ 96 - sym_idx.into(), 97 - format!("{:#x}", sym.st_value).into(), 98 - sym.st_size.into(), 99 - elf::to_str::st_symtype_to_string(sym.st_symtype()).into(), 100 - elf::to_str::st_bind_to_string(sym.st_bind()).into(), 101 - elf::to_str::st_vis_to_string(sym.st_vis()).into(), 102 - sym.st_shndx.into(), 103 - needs_name.into(), 104 - sym_name.into(), 105 - ]; 106 - table.add_row(cells); 107 - } 108 - println!("{table}"); 109 - } 110 - /* SPDX-SnippetEnd */ 111 - 112 - fn main() { 113 - let args = Args::parse(); 114 - 115 - let mut n_so_files = 0; 116 - 117 - // TODO: Restrict to only directories 118 - for wheel_dir in WalkDir::new(args.dir).min_depth(1).max_depth(1) { 119 - let wheel_dir = wheel_dir.expect("could not read file"); 120 - let wheel_dir_path = wheel_dir.path().to_str().expect("could not convert path to str"); 121 - 122 - let mut name = None; 123 - 124 - for dist_info_candidate in WalkDir::new(wheel_dir_path).min_depth(1).max_depth(1) { 125 - let dist_info_candidate = dist_info_candidate.expect("could not read file"); 126 - let dist_info_candidate_path = dist_info_candidate.path().to_str().expect("could not convert path to str"); 127 - if dist_info_candidate_path.ends_with(".dist-info") { 128 - let metadata_path = format!("{}/METADATA", dist_info_candidate_path); 129 - let metadata_lines = read_lines(&metadata_path).expect("could not read METADATA"); 130 - for line in metadata_lines.map_while(Result::ok) { 131 - if line.starts_with("Name: ") { 132 - name = Some(line.replace("Name: ", "")); 133 - } 134 - } 135 - } 136 - } 137 - 138 - if let Some(name) = name { 139 - println!("→ {}", name); 140 - 141 - for file in WalkDir::new(wheel_dir_path) { 142 - let file = file.expect("could not read file"); 143 - let path = file.path().to_str().expect("could not convert path to str"); 144 - 145 - let is_so = path.ends_with(".so") || path.contains(".so."); 146 - if is_so { 147 - println!("\t{}", path); 148 - n_so_files += 1; 149 - // let pathbuf = std::path::PathBuf::from(path); 150 - // let file_stream = File::open(pathbuf).expect("could not open file"); 151 - // let mut elf_file = 152 - // ElfStream::<AnyEndian, _>::open_stream(file_stream).expect("could not open ELF stream"); 153 - // print_dynamic_symbol_table(&mut elf_file); 154 - } 155 - } 156 - } else { 157 - panic!("Could not get name for {}", wheel_dir_path); 158 - } 159 - } 160 - 161 - println!("found {} .so files", n_so_files); 162 - }
+87
src/elf_utils.rs
··· 1 + use comfy_table::{Cell, Table}; 2 + use elf::ElfStream; 3 + use elf::endian::AnyEndian; 4 + use std::fs::File; 5 + 6 + /* 7 + From https://github.com/cole14/rust-readelf/blob/419f78ac79d2e5b538ffe28777b54a487d953840/src/rust-readelf.rs 8 + SPDX-SnippetBegin 9 + SPDX-License-Identifier: MIT 10 + SPDX-SnippetCopyrightText: Christopher Cole 11 + */ 12 + #[allow(dead_code)] 13 + pub fn print_dynamic_symbol_table(elf_file: &mut ElfStream<AnyEndian, File>) { 14 + // Get the .dynsym table. If this file doesn't have one, then we're done 15 + let (dynsyms, dynstrs) = match elf_file 16 + .dynamic_symbol_table() 17 + .expect("failed to get .dynsym and string table") 18 + { 19 + Some(tables) => tables, 20 + None => { 21 + return; 22 + } 23 + }; 24 + 25 + // Parse out all the symbols so that we can look up versions for them if needed. 26 + let symbols: Vec<(String, elf::symbol::Symbol)> = dynsyms 27 + .iter() 28 + .map(|sym| { 29 + ( 30 + dynstrs 31 + .get(sym.st_name as usize) 32 + .expect("failed to get symbol name") 33 + .to_string(), 34 + sym, 35 + ) 36 + }) 37 + .collect(); 38 + 39 + let vertab = elf_file 40 + .symbol_version_table() 41 + .expect("failed to parse GNU symbol versions"); 42 + 43 + let mut table = Table::new(); 44 + table.set_header([ 45 + "ndx", 46 + "value", 47 + "size", 48 + "type", 49 + "bind", 50 + "visibility", 51 + "shndx", 52 + "needs version", 53 + "name", 54 + ]); 55 + for (sym_idx, (sym_name, sym)) in symbols.iter().enumerate() { 56 + let needs_name = match &vertab { 57 + Some(vertab) => { 58 + if sym.is_undefined() { 59 + match vertab 60 + .get_requirement(sym_idx) 61 + .expect("failed to parse symbol requirement") 62 + { 63 + Some(req) => req.name, 64 + None => "None", 65 + } 66 + } else { 67 + "None" 68 + } 69 + } 70 + None => "None", 71 + }; 72 + let cells: Vec<Cell> = vec![ 73 + sym_idx.into(), 74 + format!("{:#x}", sym.st_value).into(), 75 + sym.st_size.into(), 76 + elf::to_str::st_symtype_to_string(sym.st_symtype()).into(), 77 + elf::to_str::st_bind_to_string(sym.st_bind()).into(), 78 + elf::to_str::st_vis_to_string(sym.st_vis()).into(), 79 + sym.st_shndx.into(), 80 + needs_name.into(), 81 + sym_name.into(), 82 + ]; 83 + table.add_row(cells); 84 + } 85 + println!("{table}"); 86 + } 87 + /* SPDX-SnippetEnd */
+1
src/lib.rs
··· 1 + pub mod elf_utils;