tugger_binary_analysis/
audit.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
5//! Analyze binaries for distribution compatibility.
6
7use {
8    crate::{
9        find_minimum_distro_version, find_undefined_elf_symbols, UndefinedSymbol,
10        GCC_VERSIONS_BY_DISTRO, GLIBC_VERSIONS_BY_DISTRO, LSB_SHARED_LIBRARIES,
11    },
12    std::{collections::BTreeMap, fs::File, io::Read, path::PathBuf},
13};
14
15pub fn analyze_file(path: PathBuf) {
16    let mut fd = File::open(path).unwrap();
17    let mut buffer = Vec::new();
18    fd.read_to_end(&mut buffer).unwrap();
19    analyze_data(&buffer);
20}
21
22pub fn analyze_data(buffer: &[u8]) {
23    match goblin::Object::parse(buffer).unwrap() {
24        goblin::Object::Elf(elf) => {
25            let mut undefined_symbols = find_undefined_elf_symbols(buffer, &elf);
26            undefined_symbols.sort();
27
28            analyze_elf_libraries(&elf.libraries, &undefined_symbols);
29        }
30        goblin::Object::PE(_pe) => {
31            panic!("PE not yet supported");
32        }
33        goblin::Object::Mach(_mach) => {
34            panic!("mach not yet supported");
35        }
36        goblin::Object::Archive(_archive) => {
37            panic!("archive not yet supported");
38        }
39        goblin::Object::Unknown(magic) => panic!("unknown magic: {:#x}", magic),
40    }
41}
42
43pub fn analyze_elf_libraries(libs: &[&str], undefined_symbols: &[UndefinedSymbol]) {
44    let mut latest_symbols: BTreeMap<String, version_compare::Version> = BTreeMap::new();
45
46    println!("Shared Library Dependencies");
47    println!("===========================");
48
49    let mut libs = libs.to_vec();
50    libs.sort_unstable();
51    for lib in libs {
52        println!("{}", lib);
53
54        if LSB_SHARED_LIBRARIES.contains(&lib) {
55            println!("  OK - Library part of Linux Standard Base and present on most distros");
56        } else {
57            println!("  PROBLEMATIC - Shared library dependency may not be on all machines");
58        }
59
60        let mut symbols: Vec<&UndefinedSymbol> = Vec::new();
61
62        for symbol in undefined_symbols {
63            if symbol.filename == Some((*lib).to_string()) {
64                symbols.push(symbol);
65            }
66        }
67
68        /*
69        println!("");
70        println!("  Symbols");
71        println!("  -------");
72        */
73
74        for symbol in symbols {
75            match &symbol.version {
76                Some(version) => {
77                    let parts: Vec<&str> = version.splitn(2, '_').collect();
78
79                    match parts.len() {
80                        1 => { /* TODO this is weird. Do something? */ }
81                        2 => {
82                            let v = version_compare::Version::from(parts[1])
83                                .expect("unable to parse version");
84
85                            match latest_symbols.get(parts[0]) {
86                                Some(existing) => {
87                                    if &v > existing {
88                                        latest_symbols.insert(parts[0].to_string(), v);
89                                    }
90                                }
91                                None => {
92                                    latest_symbols.insert(parts[0].to_string(), v);
93                                }
94                            }
95                        }
96                        _ => {}
97                    }
98
99                    //println!("  {}@{}", &symbol.symbol, version)
100                }
101                None => {
102                    //println!("  {}", &symbol.symbol)
103                }
104            }
105        }
106
107        println!();
108    }
109
110    println!("Symbol Versioning");
111    println!("=================");
112
113    for (name, version) in &latest_symbols {
114        match name.as_str() {
115            "GLIBC" => {
116                println!();
117                println!("glibc");
118                println!("-----");
119                println!();
120                println!("Minimum Version: {}", version);
121                println!("Minimum Distro Versions:");
122
123                for s in find_minimum_distro_version(version, &GLIBC_VERSIONS_BY_DISTRO) {
124                    println!("  {}", s);
125                }
126            }
127            "GCC" => {
128                println!();
129                println!("gcc");
130                println!("-----");
131                println!();
132                println!("Minimum Version: {}", version);
133                println!("Minimum Distro Versions:");
134
135                for s in find_minimum_distro_version(version, &GCC_VERSIONS_BY_DISTRO) {
136                    println!("  {}", s);
137                }
138            }
139            other => {
140                println!();
141                println!("{}", other);
142                println!("-----");
143                println!();
144                println!("Minimum Version: {}", version);
145                println!("Minimum Distro Versions: Unknown");
146            }
147        }
148    }
149}