From fdddd832240efc0f1966c90aab75ad24a5d12fac Mon Sep 17 00:00:00 2001 From: Amos Wenger Date: Thu, 21 Jul 2022 13:13:24 +0200 Subject: [PATCH] Assert that sysroot ABI version matches exactly Otherwise, fall back to the multi ABI scheme, except in testing, where it becomes a hard error. This should make it possible to use a rustup-provided rust-analyzer with proc macro dylibs compiled by older rustcs, and it'll also catch changes to the format of `rustc --version` or the `.rustc` section that would make them impossible to compare for equality. --- crates/proc-macro-api/src/lib.rs | 2 +- crates/proc-macro-api/src/version.rs | 2 +- crates/proc-macro-srv/build.rs | 25 ++++++++++++++++++ crates/proc-macro-srv/src/abis/mod.rs | 37 +++++++++++++++++++++++---- crates/proc-macro-srv/src/dylib.rs | 5 ++-- 5 files changed, 62 insertions(+), 9 deletions(-) create mode 100644 crates/proc-macro-srv/build.rs diff --git a/crates/proc-macro-api/src/lib.rs b/crates/proc-macro-api/src/lib.rs index dbf2fb37e75..d7010e825aa 100644 --- a/crates/proc-macro-api/src/lib.rs +++ b/crates/proc-macro-api/src/lib.rs @@ -26,7 +26,7 @@ use crate::{ process::ProcMacroProcessSrv, }; -pub use version::{read_dylib_info, RustCInfo}; +pub use version::{read_dylib_info, read_version, RustCInfo}; #[derive(Copy, Clone, Eq, PartialEq, Debug, Serialize, Deserialize)] pub enum ProcMacroKind { diff --git a/crates/proc-macro-api/src/version.rs b/crates/proc-macro-api/src/version.rs index 66fe16e94f7..8cef971f332 100644 --- a/crates/proc-macro-api/src/version.rs +++ b/crates/proc-macro-api/src/version.rs @@ -102,7 +102,7 @@ fn read_section<'a>(dylib_binary: &'a [u8], section_name: &str) -> io::Result<&' /// * [some more bytes that we don't really care but about still there] :-) /// Check this issue for more about the bytes layout: /// -fn read_version(dylib_path: &AbsPath) -> io::Result { +pub fn read_version(dylib_path: &AbsPath) -> io::Result { let dylib_file = File::open(dylib_path)?; let dylib_mmaped = unsafe { Mmap::map(&dylib_file) }?; diff --git a/crates/proc-macro-srv/build.rs b/crates/proc-macro-srv/build.rs new file mode 100644 index 00000000000..e3e4b9d87ba --- /dev/null +++ b/crates/proc-macro-srv/build.rs @@ -0,0 +1,25 @@ +use std::{env, fs::File, io::Write, path::PathBuf, process::Command}; + +fn main() { + // Determine rustc version `proc-macro-srv` (and thus the sysroot ABI) is + // build with and make it accessible at runtime for ABI selection. + + let mut path = PathBuf::from(env::var_os("OUT_DIR").unwrap()); + path.push("rustc_version.rs"); + let mut f = File::create(&path).unwrap(); + + let rustc = env::var("RUSTC").expect("proc-macro-srv's build script expects RUSTC to be set"); + let output = Command::new(rustc).arg("--version").output().expect("rustc --version must run"); + let version_string = std::str::from_utf8(&output.stdout[..]) + .expect("rustc --version output must be UTF-8") + .trim(); + + write!( + f, + " + #[allow(dead_code)] + pub(crate) const RUSTC_VERSION_STRING: &str = {version_string:?}; + " + ) + .unwrap(); +} diff --git a/crates/proc-macro-srv/src/abis/mod.rs b/crates/proc-macro-srv/src/abis/mod.rs index f1083a9284b..a59da0f6b10 100644 --- a/crates/proc-macro-srv/src/abis/mod.rs +++ b/crates/proc-macro-srv/src/abis/mod.rs @@ -29,6 +29,9 @@ mod abi_1_64; #[cfg(feature = "sysroot-abi")] mod abi_sysroot; +// see `build.rs` +include!(concat!(env!("OUT_DIR"), "/rustc_version.rs")); + // Used by `test/utils.rs` #[cfg(test)] pub(crate) use abi_1_64::TokenStream as TestTokenStream; @@ -74,13 +77,37 @@ impl Abi { lib: &Library, symbol_name: String, info: RustCInfo, + #[cfg_attr(not(feature = "sysroot-abi"), allow(unused_variables))] version_string: String, ) -> Result { - // Gated behind an env var for now to avoid a change in behavior for - // rustup-installed rust-analyzer + // the sysroot ABI relies on `extern proc_macro` with unstable features, + // instead of a snapshot of the proc macro bridge's source code. it's only + // enabled if we have an exact version match. #[cfg(feature = "sysroot-abi")] - if std::env::var("PROC_MACRO_SRV_SYSROOT_ABI").is_ok() { - let inner = unsafe { Abi_Sysroot::from_lib(lib, symbol_name) }?; - return Ok(Abi::AbiSysroot(inner)); + { + if version_string == RUSTC_VERSION_STRING { + let inner = unsafe { Abi_Sysroot::from_lib(lib, symbol_name) }?; + return Ok(Abi::AbiSysroot(inner)); + } + + // if we reached this point, versions didn't match. in testing, we + // want that to panic - this could mean that the format of `rustc + // --version` no longer matches the format of the version string + // stored in the `.rustc` section, and we want to catch that in-tree + // with `x.py test` + #[cfg(test)] + { + let allow_mismatch = std::env::var("PROC_MACRO_SRV_ALLOW_SYSROOT_MISMATCH"); + if let Ok("1") = allow_mismatch.as_deref() { + // only used by rust-analyzer developers, when working on the + // sysroot ABI from the rust-analyzer repository - which should + // only happen pre-subtree. this can be removed later. + } else { + panic!( + "sysroot ABI mismatch: dylib rustc version (read from .rustc section): {:?} != proc-macro-srv version (read from 'rustc --version'): {:?}", + version_string, RUSTC_VERSION_STRING + ); + } + } } // FIXME: this should use exclusive ranges when they're stable diff --git a/crates/proc-macro-srv/src/dylib.rs b/crates/proc-macro-srv/src/dylib.rs index 2b6c070fece..6439fb2130b 100644 --- a/crates/proc-macro-srv/src/dylib.rs +++ b/crates/proc-macro-srv/src/dylib.rs @@ -12,7 +12,7 @@ use libloading::Library; use memmap2::Mmap; use object::Object; use paths::AbsPath; -use proc_macro_api::{read_dylib_info, ProcMacroKind}; +use proc_macro_api::{read_dylib_info, read_version, ProcMacroKind}; use super::abis::Abi; @@ -122,9 +122,10 @@ impl ProcMacroLibraryLibloading { invalid_data_err(format!("expected an absolute path, got {}", file.display())) })?; let version_info = read_dylib_info(abs_file)?; + let version_string = read_version(abs_file)?; let lib = load_library(file).map_err(invalid_data_err)?; - let abi = Abi::from_lib(&lib, symbol_name, version_info)?; + let abi = Abi::from_lib(&lib, symbol_name, version_info, version_string)?; Ok(ProcMacroLibraryLibloading { _lib: lib, abi }) } }