1
Fork 0

Unify dylib loading between proc macros and codegen backends

As bonus this makes the errors when failing to load a proc macro more
informative to match the backend loading errors. In addition it makes it
slightly easier to patch rustc to work on platforms that don't support
dynamic linking like wasm.
This commit is contained in:
bjorn3 2024-02-21 11:17:07 +00:00
parent 0987e41d1c
commit f25c90a83f
9 changed files with 60 additions and 45 deletions

View file

@ -45,7 +45,7 @@ metadata_crate_not_panic_runtime =
the crate `{$crate_name}` is not a panic runtime
metadata_dl_error =
{$err}
{$path}{$err}
metadata_empty_link_name =
link name must not be empty

View file

@ -692,20 +692,8 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
path: &Path,
stable_crate_id: StableCrateId,
) -> Result<&'static [ProcMacro], CrateError> {
// Make sure the path contains a / or the linker will search for it.
let path = try_canonicalize(path).unwrap();
let lib = load_dylib(&path, 5).map_err(|err| CrateError::DlOpen(err))?;
let sym_name = self.sess.generate_proc_macro_decls_symbol(stable_crate_id);
let sym = unsafe { lib.get::<*const &[ProcMacro]>(sym_name.as_bytes()) }
.map_err(|err| CrateError::DlSym(err.to_string()))?;
// Intentionally leak the dynamic library. We can't ever unload it
// since the library can make things that will live arbitrarily long.
let sym = unsafe { sym.into_raw() };
std::mem::forget(lib);
Ok(unsafe { **sym })
Ok(unsafe { *load_symbol_from_dylib::<*const &[ProcMacro]>(path, &sym_name)? })
}
fn inject_panic_runtime(&mut self, krate: &ast::Crate) {
@ -1116,6 +1104,10 @@ fn alloc_error_handler_spans(krate: &ast::Crate) -> Vec<Span> {
f.spans
}
fn format_dlopen_err(e: &(dyn std::error::Error + 'static)) -> String {
e.sources().map(|e| format!(": {e}")).collect()
}
// On Windows the compiler would sometimes intermittently fail to open the
// proc-macro DLL with `Error::LoadLibraryExW`. It is suspected that something in the
// system still holds a lock on the file, so we retry a few times before calling it
@ -1154,9 +1146,43 @@ fn load_dylib(path: &Path, max_attempts: usize) -> Result<libloading::Library, S
let last_error = last_error.unwrap();
let message = if let Some(src) = last_error.source() {
format!("{last_error} ({src}) (retried {max_attempts} times)")
format!("{} ({src}) (retried {max_attempts} times)", format_dlopen_err(&last_error))
} else {
format!("{last_error} (retried {max_attempts} times)")
format!("{} (retried {max_attempts} times)", format_dlopen_err(&last_error))
};
Err(message)
}
pub enum DylibError {
DlOpen(String, String),
DlSym(String, String),
}
impl From<DylibError> for CrateError {
fn from(err: DylibError) -> CrateError {
match err {
DylibError::DlOpen(path, err) => CrateError::DlOpen(path, err),
DylibError::DlSym(path, err) => CrateError::DlSym(path, err),
}
}
}
pub unsafe fn load_symbol_from_dylib<T: Copy>(
path: &Path,
sym_name: &str,
) -> Result<T, DylibError> {
// Make sure the path contains a / or the linker will search for it.
let path = try_canonicalize(path).unwrap();
let lib =
load_dylib(&path, 5).map_err(|err| DylibError::DlOpen(path.display().to_string(), err))?;
let sym = unsafe { lib.get::<T>(sym_name.as_bytes()) }
.map_err(|err| DylibError::DlSym(path.display().to_string(), format_dlopen_err(&err)))?;
// Intentionally leak the dynamic library. We can't ever unload it
// since the library can make things that will live arbitrarily long.
let sym = unsafe { sym.into_raw() };
std::mem::forget(lib);
Ok(*sym)
}

View file

@ -535,6 +535,7 @@ pub struct StableCrateIdCollision {
pub struct DlError {
#[primary_span]
pub span: Span,
pub path: String,
pub err: String,
}

View file

@ -3,6 +3,7 @@
#![feature(rustdoc_internals)]
#![allow(internal_features)]
#![feature(decl_macro)]
#![feature(error_iter)]
#![feature(extract_if)]
#![feature(coroutines)]
#![feature(generic_nonzero)]
@ -39,6 +40,7 @@ pub mod errors;
pub mod fs;
pub mod locator;
pub use creader::{load_symbol_from_dylib, DylibError};
pub use fs::{emit_wrapper_file, METADATA_FILENAME};
pub use native_libs::find_native_static_library;
pub use rmeta::{encode_metadata, rendered_const, EncodedMetadata, METADATA_HEADER};

View file

@ -921,8 +921,8 @@ pub(crate) enum CrateError {
MultipleCandidates(Symbol, CrateFlavor, Vec<PathBuf>),
SymbolConflictsCurrent(Symbol),
StableCrateIdCollision(Symbol, Symbol),
DlOpen(String),
DlSym(String),
DlOpen(String, String),
DlSym(String, String),
LocatorCombined(Box<CombinedLocatorError>),
NotFound(Symbol),
}
@ -967,8 +967,8 @@ impl CrateError {
CrateError::StableCrateIdCollision(crate_name0, crate_name1) => {
dcx.emit_err(errors::StableCrateIdCollision { span, crate_name0, crate_name1 });
}
CrateError::DlOpen(s) | CrateError::DlSym(s) => {
dcx.emit_err(errors::DlError { span, err: s });
CrateError::DlOpen(path, err) | CrateError::DlSym(path, err) => {
dcx.emit_err(errors::DlError { span, path, err });
}
CrateError::LocatorCombined(locator) => {
let crate_name = locator.crate_name;