kernel_abi_check/python_abi/
mod.rs1use std::collections::{BTreeSet, HashMap};
2
3use eyre::Result;
4use object::{BinaryFormat, ObjectSymbol, Symbol};
5use once_cell::sync::Lazy;
6use serde::Deserialize;
7
8use crate::version::Version;
9
10static ABI_TOML: &str = include_str!("stable_abi.toml");
11
12#[derive(Deserialize)]
13struct AbiInfoSerde {
14 added: Version,
15}
16
17#[derive(Deserialize)]
18struct StableAbiSerde {
19 function: HashMap<String, AbiInfoSerde>,
20 data: HashMap<String, AbiInfoSerde>,
21}
22
23#[derive(Clone, Copy, Debug)]
24pub enum SymbolType {
25 Data,
26 Function,
27}
28
29#[derive(Clone, Debug)]
30pub struct AbiInfo {
31 #[allow(dead_code)]
32 pub symbol_type: SymbolType,
33 pub added: Version,
34}
35
36pub static PYTHON_STABLE_ABI: Lazy<HashMap<String, AbiInfo>> = Lazy::new(|| {
37 let deserialized: StableAbiSerde = toml::de::from_str(ABI_TOML).unwrap();
38 let mut symbols = HashMap::new();
39 for (name, abi) in deserialized.function {
40 symbols.insert(
41 name,
42 AbiInfo {
43 symbol_type: SymbolType::Function,
44 added: abi.added,
45 },
46 );
47 }
48 for (name, abi) in deserialized.data {
49 symbols.insert(
50 name,
51 AbiInfo {
52 symbol_type: SymbolType::Data,
53 added: abi.added,
54 },
55 );
56 }
57 symbols
58});
59
60#[derive(Debug, Clone, Eq, Ord, PartialEq, PartialOrd)]
62pub enum PythonAbiViolation {
63 IncompatibleAbi3Symbol { name: String, added: Version },
65
66 NonAbi3Symbol { name: String },
68}
69
70pub fn check_python_abi<'a>(
72 python_abi: &Version,
73 binary_format: BinaryFormat,
74 symbols: impl IntoIterator<Item = Symbol<'a, 'a>>,
75) -> Result<BTreeSet<PythonAbiViolation>> {
76 let mut violations = BTreeSet::new();
77 for symbol in symbols {
78 if !symbol.is_undefined() {
79 continue;
80 }
81
82 let mut symbol_name = symbol.name()?;
83
84 if matches!(binary_format, BinaryFormat::MachO) {
85 symbol_name = symbol_name.strip_prefix("_").unwrap_or(symbol_name);
87 }
88
89 match PYTHON_STABLE_ABI.get(symbol_name) {
90 Some(abi_info) => {
91 if &abi_info.added > python_abi {
92 violations.insert(PythonAbiViolation::IncompatibleAbi3Symbol {
93 name: symbol_name.to_string(),
94 added: abi_info.added.clone(),
95 });
96 }
97 }
98 None => {
99 if symbol_name.starts_with("Py") || symbol_name.starts_with("_Py") {
100 violations.insert(PythonAbiViolation::NonAbi3Symbol {
101 name: symbol_name.to_string(),
102 });
103 }
104 }
105 }
106 }
107
108 Ok(violations)
109}