kernel_abi_check/manylinux/
mod.rs1use 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#[derive(Debug, Clone, Eq, Ord, PartialEq, PartialOrd)]
39pub enum ManylinuxViolation {
40 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 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}