kernel_abi_check/manylinux/
mod.rs

1use std::collections::{BTreeSet, HashSet};
2use std::env::consts;
3use std::str;
4use std::{collections::HashMap, str::FromStr};
5
6use eyre::{Context, ContextCompat, OptionExt, Result};
7use object::{ObjectSymbol, Symbol};
8use once_cell::sync::Lazy;
9use serde::Deserialize;
10
11use crate::Version;
12
13#[derive(Debug, Deserialize)]
14struct ManyLinux {
15    name: String,
16    #[allow(dead_code)]
17    aliases: Vec<String>,
18    #[allow(dead_code)]
19    priority: u32,
20    symbol_versions: HashMap<String, HashMap<String, HashSet<String>>>,
21    #[allow(dead_code)]
22    lib_whitelist: Vec<String>,
23    #[allow(dead_code)]
24    blacklist: HashMap<String, Vec<String>>,
25}
26
27static MANYLINUX_POLICY_JSON: &str = include_str!("manylinux-policy.json");
28
29static MANYLINUX_VERSIONS: Lazy<HashMap<String, ManyLinux>> = Lazy::new(|| {
30    let deserialized: Vec<ManyLinux> = serde_json::from_str(MANYLINUX_POLICY_JSON).unwrap();
31    deserialized
32        .into_iter()
33        .map(|manylinux| (manylinux.name.clone(), manylinux))
34        .collect()
35});
36
37/// A violation of the manylinux policy.
38#[derive(Debug, Clone, Eq, Ord, PartialEq, PartialOrd)]
39pub enum ManylinuxViolation {
40    /// A symbol is not allowed in the manylinux version.
41    Symbol {
42        name: String,
43        dep: String,
44        version: String,
45    },
46}
47
48pub fn check_manylinux<'a>(
49    manylinux_version: &str,
50    symbols: impl IntoIterator<Item = Symbol<'a, 'a>>,
51) -> Result<BTreeSet<ManylinuxViolation>> {
52    let symbol_versions = MANYLINUX_VERSIONS
53        .get(manylinux_version)
54        .context(format!("Unknown manylinux version: {}", manylinux_version))?
55        .symbol_versions
56        .get(consts::ARCH)
57        .context(format!(
58            "Cannot find arch `{}` for: {}`",
59            consts::ARCH,
60            manylinux_version
61        ))?;
62
63    let mut violations = BTreeSet::new();
64
65    for symbol in symbols {
66        if symbol.is_undefined() {
67            let symbol = symbol.name_bytes().context("Cannot get symbol name")?;
68            let symbol = str::from_utf8(symbol).context("Cannot parse symbol name as UTF-8")?;
69
70            let mut symbol_parts = symbol.split('@');
71            let symbol_name = symbol_parts.next().context("Cannot get symbol name")?;
72
73            let version_info = match symbol_parts.next() {
74                Some(version_info) => version_info,
75                None => continue,
76            };
77
78            let mut version_parts = version_info.split('_');
79
80            let dep = version_parts
81                .next()
82                .ok_or_eyre("Cannot get symbol version name")?;
83
84            let version = match version_parts.next() {
85                Some(version) => Version::from_str(version)?,
86                // We also get symbol versions like: libcudart.so.12
87                None => continue,
88            };
89
90            if let Some(versions) = symbol_versions.get(dep) {
91                if !versions.contains(&version.to_string()) {
92                    violations.insert(ManylinuxViolation::Symbol {
93                        name: symbol_name.to_string(),
94                        dep: dep.to_string(),
95                        version: version.to_string(),
96                    });
97                }
98            }
99        }
100    }
101
102    Ok(violations)
103}