Skip to main content

rustc_metadata/
host_dylib.rs

1use std::error::Error;
2use std::path::Path;
3use std::time::Duration;
4
5use rustc_fs_util::try_canonicalize;
6use rustc_proc_macro::bridge::client::Client as ProcMacroClient;
7use rustc_session::StableCrateId;
8use tracing::debug;
9
10use crate::locator::CrateError;
11
12fn format_dlopen_err(e: &(dyn std::error::Error + 'static)) -> String {
13    e.sources().map(|e| ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!(": {0}", e))
    })format!(": {e}")).collect()
14}
15
16fn attempt_load_dylib(path: &Path) -> Result<libloading::Library, libloading::Error> {
17    #[cfg(target_os = "aix")]
18    if let Some(ext) = path.extension()
19        && ext.eq("a")
20    {
21        // On AIX, we ship all libraries as .a big_af archive
22        // the expected format is lib<name>.a(libname.so) for the actual
23        // dynamic library
24        let library_name = path.file_stem().expect("expect a library name");
25        let mut archive_member = std::ffi::OsString::from("a(");
26        archive_member.push(library_name);
27        archive_member.push(".so)");
28        let new_path = path.with_extension(archive_member);
29
30        // On AIX, we need RTLD_MEMBER to dlopen an archived shared
31        let flags = libc::RTLD_LAZY | libc::RTLD_LOCAL | libc::RTLD_MEMBER;
32        return unsafe { libloading::os::unix::Library::open(Some(&new_path), flags) }
33            .map(|lib| lib.into());
34    }
35
36    unsafe { libloading::Library::new(&path) }
37}
38
39// On Windows the compiler would sometimes intermittently fail to open the
40// proc-macro DLL with `Error::LoadLibraryExW`. It is suspected that something in the
41// system still holds a lock on the file, so we retry a few times before calling it
42// an error.
43fn load_dylib(path: &Path, max_attempts: usize) -> Result<libloading::Library, String> {
44    if !(max_attempts > 0) {
    ::core::panicking::panic("assertion failed: max_attempts > 0")
};assert!(max_attempts > 0);
45
46    let mut last_error = None;
47
48    for attempt in 0..max_attempts {
49        {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_metadata/src/host_dylib.rs:49",
                        "rustc_metadata::host_dylib", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_metadata/src/host_dylib.rs"),
                        ::tracing_core::__macro_support::Option::Some(49u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_metadata::host_dylib"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                let mut iter = __CALLSITE.metadata().fields().iter();
                __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&format_args!("Attempt to load proc-macro `{0}`.",
                                                    path.display()) as &dyn Value))])
            });
    } else { ; }
};debug!("Attempt to load proc-macro `{}`.", path.display());
50        match attempt_load_dylib(path) {
51            Ok(lib) => {
52                if attempt > 0 {
53                    {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_metadata/src/host_dylib.rs:53",
                        "rustc_metadata::host_dylib", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_metadata/src/host_dylib.rs"),
                        ::tracing_core::__macro_support::Option::Some(53u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_metadata::host_dylib"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                let mut iter = __CALLSITE.metadata().fields().iter();
                __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&format_args!("Loaded proc-macro `{0}` after {1} attempts.",
                                                    path.display(), attempt + 1) as &dyn Value))])
            });
    } else { ; }
};debug!(
54                        "Loaded proc-macro `{}` after {} attempts.",
55                        path.display(),
56                        attempt + 1
57                    );
58                }
59                return Ok(lib);
60            }
61            Err(err) => {
62                // Only try to recover from this specific error.
63                if !#[allow(non_exhaustive_omitted_patterns)] match err {
    libloading::Error::LoadLibraryExW { .. } => true,
    _ => false,
}matches!(err, libloading::Error::LoadLibraryExW { .. }) {
64                    {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_metadata/src/host_dylib.rs:64",
                        "rustc_metadata::host_dylib", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_metadata/src/host_dylib.rs"),
                        ::tracing_core::__macro_support::Option::Some(64u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_metadata::host_dylib"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                let mut iter = __CALLSITE.metadata().fields().iter();
                __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&format_args!("Failed to load proc-macro `{0}`. Not retrying",
                                                    path.display()) as &dyn Value))])
            });
    } else { ; }
};debug!("Failed to load proc-macro `{}`. Not retrying", path.display());
65                    let err = format_dlopen_err(&err);
66                    // We include the path of the dylib in the error ourselves, so
67                    // if it's in the error, we strip it.
68                    if let Some(err) = err.strip_prefix(&::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!(": {0}", path.display()))
    })format!(": {}", path.display())) {
69                        return Err(err.to_string());
70                    }
71                    return Err(err);
72                }
73
74                last_error = Some(err);
75                std::thread::sleep(Duration::from_millis(100));
76                {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_metadata/src/host_dylib.rs:76",
                        "rustc_metadata::host_dylib", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_metadata/src/host_dylib.rs"),
                        ::tracing_core::__macro_support::Option::Some(76u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_metadata::host_dylib"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                let mut iter = __CALLSITE.metadata().fields().iter();
                __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&format_args!("Failed to load proc-macro `{0}`. Retrying.",
                                                    path.display()) as &dyn Value))])
            });
    } else { ; }
};debug!("Failed to load proc-macro `{}`. Retrying.", path.display());
77            }
78        }
79    }
80
81    {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_metadata/src/host_dylib.rs:81",
                        "rustc_metadata::host_dylib", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_metadata/src/host_dylib.rs"),
                        ::tracing_core::__macro_support::Option::Some(81u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_metadata::host_dylib"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                let mut iter = __CALLSITE.metadata().fields().iter();
                __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&format_args!("Failed to load proc-macro `{0}` even after {1} attempts.",
                                                    path.display(), max_attempts) as &dyn Value))])
            });
    } else { ; }
};debug!("Failed to load proc-macro `{}` even after {} attempts.", path.display(), max_attempts);
82
83    let last_error = last_error.unwrap();
84    let message = if let Some(src) = last_error.source() {
85        ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0} ({1}) (retried {2} times)",
                format_dlopen_err(&last_error), src, max_attempts))
    })format!("{} ({src}) (retried {max_attempts} times)", format_dlopen_err(&last_error))
86    } else {
87        ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0} (retried {1} times)",
                format_dlopen_err(&last_error), max_attempts))
    })format!("{} (retried {max_attempts} times)", format_dlopen_err(&last_error))
88    };
89    Err(message)
90}
91
92pub enum DylibError {
93    DlOpen(String, String),
94    DlSym(String, String),
95}
96
97impl From<DylibError> for CrateError {
98    fn from(err: DylibError) -> CrateError {
99        match err {
100            DylibError::DlOpen(path, err) => CrateError::DlOpen(path, err),
101            DylibError::DlSym(path, err) => CrateError::DlSym(path, err),
102        }
103    }
104}
105
106pub unsafe fn load_symbol_from_dylib<T: Copy>(
107    path: &Path,
108    sym_name: &str,
109) -> Result<T, DylibError> {
110    // Make sure the path contains a / or the linker will search for it.
111    let path = try_canonicalize(path).unwrap();
112    let lib =
113        load_dylib(&path, 5).map_err(|err| DylibError::DlOpen(path.display().to_string(), err))?;
114
115    let sym = unsafe { lib.get::<T>(sym_name.as_bytes()) }
116        .map_err(|err| DylibError::DlSym(path.display().to_string(), format_dlopen_err(&err)))?;
117
118    // Intentionally leak the dynamic library. We can't ever unload it
119    // since the library can make things that will live arbitrarily long.
120    let sym = unsafe { sym.into_raw() };
121    std::mem::forget(lib);
122
123    Ok(*sym)
124}
125
126pub(crate) fn dlsym_proc_macros(
127    path: &Path,
128    stable_crate_id: StableCrateId,
129) -> Result<&'static [ProcMacroClient], DylibError> {
130    let sym_name = rustc_session::generate_proc_macro_decls_symbol(stable_crate_id);
131    {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_metadata/src/host_dylib.rs:131",
                        "rustc_metadata::host_dylib", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_metadata/src/host_dylib.rs"),
                        ::tracing_core::__macro_support::Option::Some(131u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_metadata::host_dylib"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                let mut iter = __CALLSITE.metadata().fields().iter();
                __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&format_args!("trying to dlsym proc_macros {0} for symbol `{1}`",
                                                    path.display(), sym_name) as &dyn Value))])
            });
    } else { ; }
};debug!("trying to dlsym proc_macros {} for symbol `{}`", path.display(), sym_name);
132
133    unsafe {
134        // FIXME(bjorn3) this depends on the unstable slice memory layout
135        let result = crate::load_symbol_from_dylib::<*const &[ProcMacroClient]>(path, &sym_name);
136        match result {
137            Ok(result) => {
138                {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_metadata/src/host_dylib.rs:138",
                        "rustc_metadata::host_dylib", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_metadata/src/host_dylib.rs"),
                        ::tracing_core::__macro_support::Option::Some(138u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_metadata::host_dylib"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                let mut iter = __CALLSITE.metadata().fields().iter();
                __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&format_args!("loaded dlsym proc_macros {0} for symbol `{1}`",
                                                    path.display(), sym_name) as &dyn Value))])
            });
    } else { ; }
};debug!("loaded dlsym proc_macros {} for symbol `{}`", path.display(), sym_name);
139                Ok(*result)
140            }
141            Err(err) => {
142                {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_metadata/src/host_dylib.rs:142",
                        "rustc_metadata::host_dylib", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_metadata/src/host_dylib.rs"),
                        ::tracing_core::__macro_support::Option::Some(142u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_metadata::host_dylib"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                let mut iter = __CALLSITE.metadata().fields().iter();
                __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&format_args!("failed to dlsym proc_macros {0} for symbol `{1}`",
                                                    path.display(), sym_name) as &dyn Value))])
            });
    } else { ; }
};debug!("failed to dlsym proc_macros {} for symbol `{}`", path.display(), sym_name);
143                Err(err.into())
144            }
145        }
146    }
147}