Skip to main content

std/sys/platform_version/darwin/
core_foundation.rs

1//! Minimal utilities for interfacing with a dynamically loaded CoreFoundation.
2#![allow(non_snake_case, non_upper_case_globals)]
3use super::root_relative;
4use crate::ffi::{CStr, c_char, c_void};
5use crate::ptr::null_mut;
6use crate::sys::helpers::run_path_with_cstr;
7
8// MacTypes.h
9pub(super) type Boolean = u8;
10// CoreFoundation/CFBase.h
11pub(super) type CFTypeID = usize;
12pub(super) type CFOptionFlags = usize;
13pub(super) type CFIndex = isize;
14pub(super) type CFTypeRef = *mut c_void;
15pub(super) type CFAllocatorRef = CFTypeRef;
16pub(super) const kCFAllocatorDefault: CFAllocatorRef = null_mut();
17// CoreFoundation/CFError.h
18pub(super) type CFErrorRef = CFTypeRef;
19// CoreFoundation/CFData.h
20pub(super) type CFDataRef = CFTypeRef;
21// CoreFoundation/CFPropertyList.h
22pub(super) const kCFPropertyListImmutable: CFOptionFlags = 0;
23pub(super) type CFPropertyListFormat = CFIndex;
24pub(super) type CFPropertyListRef = CFTypeRef;
25// CoreFoundation/CFString.h
26pub(super) type CFStringRef = CFTypeRef;
27pub(super) type CFStringEncoding = u32;
28pub(super) const kCFStringEncodingUTF8: CFStringEncoding = 0x08000100;
29// CoreFoundation/CFDictionary.h
30pub(super) type CFDictionaryRef = CFTypeRef;
31
32/// An open handle to the dynamically loaded CoreFoundation framework.
33///
34/// This is `dlopen`ed, and later `dlclose`d. This is done to try to avoid
35/// "leaking" the CoreFoundation symbols to the rest of the user's binary if
36/// they decided to not link CoreFoundation themselves.
37///
38/// It is also faster to look up symbols directly via this handle than with
39/// `RTLD_DEFAULT`.
40pub(super) struct CFHandle(*mut c_void);
41
42macro_rules! dlsym_fn {
43    (
44        unsafe fn $name:ident($($param:ident: $param_ty:ty),* $(,)?) $(-> $ret:ty)?;
45    ) => {
46        pub(super) unsafe fn $name(&self, $($param: $param_ty),*) $(-> $ret)? {
47            let ptr = unsafe {
48                libc::dlsym(
49                    self.0,
50                    concat!(stringify!($name), '\0').as_bytes().as_ptr().cast(),
51                )
52            };
53            if ptr.is_null() {
54                let err = unsafe { CStr::from_ptr(libc::dlerror()) };
55                panic!("could not find function {}: {err:?}", stringify!($name));
56            }
57
58            // SAFETY: Just checked that the symbol isn't NULL, and macro invoker verifies that
59            // the signature is correct.
60            let fnptr = unsafe {
61                crate::mem::transmute::<
62                    *mut c_void,
63                    unsafe extern "C" fn($($param_ty),*) $(-> $ret)?,
64                >(ptr)
65            };
66
67            // SAFETY: Upheld by caller.
68            unsafe { fnptr($($param),*) }
69        }
70    };
71}
72
73impl CFHandle {
74    /// Link to the CoreFoundation dylib, and look up symbols from that.
75    pub(super) fn new() -> Self {
76        // We explicitly use non-versioned path here, to allow this to work on older iOS devices.
77        let cf_path =
78            root_relative("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation");
79
80        let handle = run_path_with_cstr(&cf_path, &|path| unsafe {
81            Ok(libc::dlopen(path.as_ptr(), libc::RTLD_LAZY | libc::RTLD_LOCAL))
82        })
83        .expect("failed allocating string");
84
85        if handle.is_null() {
86            let err = unsafe { CStr::from_ptr(libc::dlerror()) };
87            panic!("could not open CoreFoundation.framework: {err:?}");
88        }
89
90        Self(handle)
91    }
92
93    pub(super) fn kCFAllocatorNull(&self) -> CFAllocatorRef {
94        // Available: in all CF versions.
95        let static_ptr = unsafe { libc::dlsym(self.0, c"kCFAllocatorNull".as_ptr()) };
96        if static_ptr.is_null() {
97            let err = unsafe { CStr::from_ptr(libc::dlerror()) };
98            panic!("could not find kCFAllocatorNull: {err:?}");
99        }
100        unsafe { *static_ptr.cast() }
101    }
102
103    // CoreFoundation/CFBase.h
104    dlsym_fn!(
105        // Available: in all CF versions.
106        unsafe fn CFRelease(cf: CFTypeRef);
107    );
108    dlsym_fn!(
109        // Available: in all CF versions.
110        unsafe fn CFGetTypeID(cf: CFTypeRef) -> CFTypeID;
111    );
112
113    // CoreFoundation/CFData.h
114    dlsym_fn!(
115        // Available: in all CF versions.
116        unsafe fn CFDataCreateWithBytesNoCopy(
117            allocator: CFAllocatorRef,
118            bytes: *const u8,
119            length: CFIndex,
120            bytes_deallocator: CFAllocatorRef,
121        ) -> CFDataRef;
122    );
123
124    // CoreFoundation/CFPropertyList.h
125    dlsym_fn!(
126        // Available: since macOS 10.6.
127        unsafe fn CFPropertyListCreateWithData(
128            allocator: CFAllocatorRef,
129            data: CFDataRef,
130            options: CFOptionFlags,
131            format: *mut CFPropertyListFormat,
132            error: *mut CFErrorRef,
133        ) -> CFPropertyListRef;
134    );
135
136    // CoreFoundation/CFString.h
137    dlsym_fn!(
138        // Available: in all CF versions.
139        unsafe fn CFStringGetTypeID() -> CFTypeID;
140    );
141    dlsym_fn!(
142        // Available: in all CF versions.
143        unsafe fn CFStringCreateWithCStringNoCopy(
144            alloc: CFAllocatorRef,
145            c_str: *const c_char,
146            encoding: CFStringEncoding,
147            contents_deallocator: CFAllocatorRef,
148        ) -> CFStringRef;
149    );
150    dlsym_fn!(
151        // Available: in all CF versions.
152        unsafe fn CFStringGetCString(
153            the_string: CFStringRef,
154            buffer: *mut c_char,
155            buffer_size: CFIndex,
156            encoding: CFStringEncoding,
157        ) -> Boolean;
158    );
159
160    // CoreFoundation/CFDictionary.h
161    dlsym_fn!(
162        // Available: in all CF versions.
163        unsafe fn CFDictionaryGetTypeID() -> CFTypeID;
164    );
165    dlsym_fn!(
166        // Available: in all CF versions.
167        unsafe fn CFDictionaryGetValue(
168            the_dict: CFDictionaryRef,
169            key: *const c_void,
170        ) -> *const c_void;
171    );
172}
173
174impl Drop for CFHandle {
175    fn drop(&mut self) {
176        // Ignore errors when closing. This is also what `libloading` does:
177        // https://docs.rs/libloading/0.8.6/src/libloading/os/unix/mod.rs.html#374
178        let _ = unsafe { libc::dlclose(self.0) };
179    }
180}