mc_sgx_core_build/
lib.rs

1// Copyright (c) 2022-2024 The MobileCoin Foundation
2
3#![doc = include_str!("../README.md")]
4
5use bindgen::callbacks::DeriveInfo;
6use bindgen::{
7    callbacks::{IntKind, ParseCallbacks},
8    Builder, EnumVariation,
9};
10use std::collections::BTreeSet;
11use std::{env, path::PathBuf};
12
13static DEFAULT_SGX_SDK_PATH: &str = "/opt/intel/sgxsdk";
14
15/// Type name prefixes that need the underscore prefix stripped from them.
16///
17/// For example `_foo_bar` should be `foo_bar`.
18///
19/// While it would be nice to add the `sgx` prefix to any non `_sgx` named type,
20/// the name does not propagate to the types used in the generated bindings.
21/// For example mapping `_foo_bar` to `sgx_foo_bar` would fail because the
22/// following function would still be looking for `foo_bar`.
23/// ```C
24/// void some_function(foo_bar arg);
25/// ```
26const STRIP_UNDERSCORE_PREFIX: &[&str] = &["_sgx", "_tee", "_quote3", "_pck", "_rsa"];
27
28/// Normalizes a type encountered by bindgen
29///
30/// Provides a default [bindgen::callbacks::ParseCallbacks::item_name]
31/// implementation that works with most SGX types.
32/// The type should come back in the form of `sgx_<main_text_from_c_interface>`
33///
34/// Returns `None` if the type is already normalized
35///
36/// # Arguments
37/// * `name` - The name of the type to determine the bindgen name of.
38pub fn normalize_item_name(name: &str) -> Option<String> {
39    let mut name = name.to_string();
40
41    // all of the exposed sgx types end in `_t`, but at times the underlying
42    // type may be missing it.
43    if !name.ends_with("_t") {
44        name.push_str("_t");
45    }
46
47    // Some types come through as `_sgx_thread_mutex_attr_t` which map to a real
48    // type of `sgx_thread_mutexattr_t`
49    let mut name = name.replace("_attr_", "attr_");
50
51    if STRIP_UNDERSCORE_PREFIX
52        .iter()
53        .any(|prefix| name.starts_with(prefix))
54    {
55        name.strip_prefix('_').map(str::to_string)
56    } else if name.starts_with('_') {
57        name.insert_str(0, "sgx");
58        Some(name)
59    } else {
60        None
61    }
62}
63
64/// Returns a builder configured with the defaults for using bindgen with the
65/// SGX libraries.
66pub fn sgx_builder() -> Builder {
67    let mut builder = Builder::default()
68        .derive_copy(false)
69        .derive_debug(false)
70        .default_enum_style(EnumVariation::NewType {
71            is_bitfield: false,
72            is_global: false,
73        })
74        .prepend_enum_name(false)
75        .use_core()
76        .ctypes_prefix("core::ffi")
77        .allowlist_recursively(false)
78        .clang_args(env_c_flags());
79
80    for include_path in vendored_include_paths() {
81        let include_path = include_path.display();
82        builder = builder.clang_arg(format!("-I{include_path}"));
83        cargo_emit::rerun_if_changed!(include_path);
84    }
85
86    builder
87}
88
89// Gets the `CFLAGS` from the environment, if any.  When there are no `CLFAGS`
90// will return an empty vector.
91// The `CFLAGS` will be split on whitespace in order to allow for multiple
92// arguments. This does *not* attempt to handle escaped shell characters,
93// https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html
94fn env_c_flags() -> Vec<String> {
95    let env_flags = env::var("CFLAGS").ok();
96    env_flags.map_or_else(Vec::new, |flags| {
97        flags
98            .split_whitespace()
99            .map(String::from)
100            .collect::<Vec<_>>()
101    })
102}
103
104/// SGXParseCallbacks to be used with [bindgen::Builder::parse_callbacks]
105///
106/// This provides a default implementation for most of the SGX libraries
107#[derive(Debug, Default)]
108pub struct SgxParseCallbacks {
109    // types that are copyable and thus should derive `Copy`
110    //
111    // These are usually small types or packed types
112    copyable_types: Vec<String>,
113
114    // types that need to derive `Default`
115    default_types: Vec<String>,
116
117    // types that are enums
118    enum_types: Vec<String>,
119
120    // Dynamically Sized types
121    dynamically_sized_types: Vec<String>,
122
123    // types that need to derive `Serialize` and `Deserialize`
124    serialize_types: Vec<String>,
125}
126
127impl SgxParseCallbacks {
128    /// Types that are enums
129    ///
130    /// Bindgen derives some attributes by default for enums, in order to
131    /// properly handle them they must be known.
132    ///
133    /// Note: Enums will also derive `Copy`, there is no need to specify in
134    ///       [`derive_copy()`](SgxParseCallbacks::derive_copy())
135    ///
136    /// # Arguments
137    /// * `enum_types` - Types that are enums.
138    pub fn enum_types<'a, E, I>(mut self, enum_types: I) -> Self
139    where
140        I: IntoIterator<Item = &'a E>,
141        E: ToString + 'a + ?Sized,
142    {
143        let enum_types = enum_types
144            .into_iter()
145            .map(ToString::to_string)
146            .collect::<Vec<_>>();
147        self.enum_types.extend(enum_types.clone());
148
149        // Enum types (from C interfaces) are small enough to always be
150        // copyable.
151        self.copyable_types.extend(enum_types);
152
153        self
154    }
155
156    /// Types to derive copy for, usually packed types
157    ///
158    /// # Arguments
159    /// * `copyable_types` - Types to derive copy for.
160    pub fn derive_copy<'a, E, I>(mut self, copyable_types: I) -> Self
161    where
162        I: IntoIterator<Item = &'a E>,
163        E: ToString + 'a + ?Sized,
164    {
165        self.copyable_types
166            .extend(copyable_types.into_iter().map(ToString::to_string));
167        self
168    }
169
170    /// Types to derive default for
171    ///
172    /// # Arguments
173    /// * `default_types` - Types to derive default for.
174    pub fn derive_default<'a, E, I>(mut self, default_types: I) -> Self
175    where
176        I: IntoIterator<Item = &'a E>,
177        E: ToString + 'a + ?Sized,
178    {
179        self.default_types
180            .extend(default_types.into_iter().map(ToString::to_string));
181        self
182    }
183
184    /// Dynamically Sized Types
185    ///
186    /// # Arguments
187    /// * `dynamically_sized_types` - Types that are dynamically sized.  Due to
188    ///   the dynamic size certain traits like `Eq` can't be derived.
189    pub fn dynamically_sized_types<'a, E, I>(mut self, dynamically_sized_types: I) -> Self
190    where
191        I: IntoIterator<Item = &'a E>,
192        E: ToString + 'a + ?Sized,
193    {
194        self.dynamically_sized_types
195            .extend(dynamically_sized_types.into_iter().map(ToString::to_string));
196        self
197    }
198
199    /// Types to derive serde `Serialize` and `Deserialize` for
200    pub fn serialize_types<'a, E, I>(mut self, serialize_types: I) -> Self
201    where
202        I: IntoIterator<Item = &'a E>,
203        E: ToString + 'a + ?Sized,
204    {
205        self.serialize_types
206            .extend(serialize_types.into_iter().map(ToString::to_string));
207        self
208    }
209
210    /// Get the derives that should be applied to the provided named type.
211    fn derives(&self, type_name: &str) -> Vec<String> {
212        let mut attributes = BTreeSet::new();
213
214        if self.default_types.iter().any(|n| *n == type_name) {
215            attributes.insert("Default");
216        }
217
218        // The [enum_types] method adds enums to the [copyable_types]
219        if self.copyable_types.iter().any(|n| *n == type_name) {
220            attributes.insert("Copy");
221        }
222
223        if !self.dynamically_sized_types.iter().any(|n| *n == type_name) {
224            // For dynamically sized types we don't even derive Debug, because
225            // they are often times packed and packed types can't derive Debug
226            // without deriving Copy, however by the dynamic nature one can't
227            // derive Copy
228            attributes.insert("Debug");
229            if !self.enum_types.iter().any(|n| *n == type_name) {
230                attributes.extend(["Eq", "PartialEq", "Hash", "Clone"]);
231            }
232        };
233
234        if self.serialize_types.iter().any(|n| *n == type_name) {
235            attributes.extend(["Serialize", "Deserialize"]);
236        }
237
238        attributes.into_iter().map(String::from).collect::<Vec<_>>()
239    }
240}
241
242impl ParseCallbacks for SgxParseCallbacks {
243    fn item_name(&self, name: &str) -> Option<String> {
244        normalize_item_name(name)
245    }
246
247    fn add_derives(&self, info: &DeriveInfo<'_>) -> Vec<String> {
248        self.derives(info.name)
249    }
250
251    fn int_macro(&self, name: &str, _value: i64) -> Option<IntKind> {
252        const USIZE_SUFFIXES: &[&str] = &["_SIZE", "_BYTES", "_IDX", "_COUNT"];
253        if USIZE_SUFFIXES.iter().any(|suffix| name.ends_with(suffix)) {
254            Some(IntKind::Custom {
255                name: "usize",
256                is_signed: false,
257            })
258        } else if name.starts_with("SGX_KEYSELECT_") || name.starts_with("SGX_KEYPOLICY_") {
259            Some(IntKind::U16)
260        } else {
261            None
262        }
263    }
264}
265
266/// Return the SGX SDK path, if it exists.
267fn sgx_sdk_dir() -> Option<PathBuf> {
268    env::var("SGX_SDK").ok().map(PathBuf::from)
269}
270
271/// This constant contains the manifest dir of crate-build, which will contain
272/// the headers, which allows all the headers to live in one dir, rather than
273/// scattered about the repo.
274const CARGO_MANIFEST_DIR: &str = env!("CARGO_MANIFEST_DIR");
275
276/// Return the SGX include paths
277///
278/// The paths to the vendored headers are always used.
279fn vendored_include_paths() -> Vec<PathBuf> {
280    let headers = PathBuf::from(CARGO_MANIFEST_DIR).join("headers");
281    vec![headers.join("tlibc"), headers]
282}
283
284/// Return the SGX library path.
285///
286/// Will first attempt to look at the environment variable `SGX_SDK`, if that
287/// isn't present then `/opt/intel/sgxsdk` will be used.
288pub fn sgx_library_dir() -> PathBuf {
289    // As of INTEL-SA-00615, 32-on-64bit enclaves are insecure, so we don't support
290    // them.
291    sgx_sdk_dir()
292        .unwrap_or_else(|| PathBuf::from(DEFAULT_SGX_SDK_PATH))
293        .join("lib64")
294}
295
296/// Return the SGX library path as a string
297///
298/// Calls sgx_library_dir() and converts to a string.
299pub fn sgx_library_string() -> String {
300    sgx_library_dir()
301        .to_str()
302        .expect("SGX_SDK contained invalid UTF-8 that wasn't caught by rust")
303        .to_owned()
304}
305
306/// Return the SGX binary path.
307///
308/// Will first attempt to look at the environment variable `SGX_SDK`, if that
309/// isn't present then `/opt/intel/sgxsdk` will be used.
310pub fn sgx_bin_x64_dir() -> PathBuf {
311    let mut retval = sgx_sdk_dir().unwrap_or_else(|| PathBuf::from(DEFAULT_SGX_SDK_PATH));
312    retval.push("bin");
313    retval.push("x64");
314    retval
315}
316
317/// Return the build output path.
318pub fn build_output_dir() -> PathBuf {
319    PathBuf::from(env::var("OUT_DIR").expect("Missing env.OUT_DIR"))
320}
321
322/// Return the SGX library suffix
323///
324/// Some SGX libraries have a suffix for example `sgx_trts.a` versus
325/// `sgx_trts_sim.a`.  This will result the suffix based on the presence of the
326/// feature `sim`.
327pub fn sgx_library_suffix() -> &'static str {
328    // See https://doc.rust-lang.org/cargo/reference/features.html#build-scripts
329    // for description of `CARGO_FEATURE_<name>`
330    match env::var("CARGO_FEATURE_SIM") {
331        Ok(_) => "_sim",
332        _ => "",
333    }
334}
335
336#[cfg(test)]
337mod test {
338    use super::*;
339
340    #[test]
341    fn verify_consistency_of_derive_attributes() {
342        // This test prevents a regression where `HashSet` was used to prevent
343        // duplicates in the derives. Using `HashSet` resulted in the derives
344        // coming back in varying order based on seed of the default hasher.
345        // The seed of the default hasher is randomized per process run.
346        // This inconsistent ordering of the derives prevented reproducible
347        // builds as the derived implementations would move around in the
348        // resultant binary.
349        let mut callbacks = SgxParseCallbacks::default();
350        callbacks = callbacks.serialize_types(["foo"]);
351        let derives = callbacks.derives("foo");
352
353        let expected = [
354            "Clone",
355            "Debug",
356            "Deserialize",
357            "Eq",
358            "Hash",
359            "PartialEq",
360            "Serialize",
361        ]
362        .into_iter()
363        .map(String::from)
364        .collect::<Vec<_>>();
365        assert_eq!(derives, expected);
366    }
367}