1 //! Handles dynamic library loading for proc macro
3 use crate::{proc_macro::bridge, rustc_server::TokenStream};
6 use goblin::{mach::Mach, Object};
7 use libloading::Library;
8 use ra_proc_macro::ProcMacroKind;
10 use std::io::Error as IoError;
11 use std::io::ErrorKind as IoErrorKind;
13 const NEW_REGISTRAR_SYMBOL: &str = "_rustc_proc_macro_decls_";
15 fn invalid_data_err(e: impl Into<Box<dyn std::error::Error + Send + Sync>>) -> IoError {
16 IoError::new(IoErrorKind::InvalidData, e)
19 fn get_symbols_from_lib(file: &Path) -> Result<Vec<String>, IoError> {
20 let buffer = std::fs::read(file)?;
21 let object = Object::parse(&buffer).map_err(invalid_data_err)?;
25 let symbols = elf.dynstrtab.to_vec().map_err(invalid_data_err)?;
26 let names = symbols.iter().map(|s| s.to_string()).collect();
31 pe.exports.iter().flat_map(|s| s.name).map(|n| n.to_string()).collect();
34 Object::Mach(mach) => match mach {
35 Mach::Binary(binary) => {
36 let exports = binary.exports().map_err(invalid_data_err)?;
41 // https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/dlsym.3.html
42 // Unlike other dyld API's, the symbol name passed to dlsym() must NOT be
43 // prepended with an underscore.
44 if s.name.starts_with("_") {
45 s.name[1..].to_string()
53 Mach::Fat(_) => Ok(vec![]),
55 Object::Archive(_) | Object::Unknown(_) => Ok(vec![]),
59 fn is_derive_registrar_symbol(symbol: &str) -> bool {
60 symbol.contains(NEW_REGISTRAR_SYMBOL)
63 fn find_registrar_symbol(file: &Path) -> Result<Option<String>, IoError> {
64 let symbols = get_symbols_from_lib(file)?;
65 Ok(symbols.into_iter().find(|s| is_derive_registrar_symbol(s)))
68 /// Loads dynamic library in platform dependent manner.
70 /// For unix, you have to use RTLD_DEEPBIND flag to escape problems described
71 /// [here](https://github.com/fedochet/rust-proc-macro-panic-inside-panic-expample)
72 /// and [here](https://github.com/rust-lang/rust/issues/60593).
74 /// Usage of RTLD_DEEPBIND
75 /// [here](https://github.com/fedochet/rust-proc-macro-panic-inside-panic-expample/issues/1)
77 /// It seems that on Windows that behaviour is default, so we do nothing in that case.
79 fn load_library(file: &Path) -> Result<Library, libloading::Error> {
84 fn load_library(file: &Path) -> Result<Library, libloading::Error> {
85 use libloading::os::unix::Library as UnixLibrary;
86 use std::os::raw::c_int;
88 const RTLD_NOW: c_int = 0x00002;
89 const RTLD_DEEPBIND: c_int = 0x00008;
91 UnixLibrary::open(Some(file), RTLD_NOW | RTLD_DEEPBIND).map(|lib| lib.into())
94 struct ProcMacroLibraryLibloading {
95 // Hold the dylib to prevent it for unloadeding
97 exported_macros: Vec<bridge::client::ProcMacro>,
100 impl ProcMacroLibraryLibloading {
101 fn open(file: &Path) -> Result<Self, IoError> {
102 let symbol_name = find_registrar_symbol(file)?
103 .ok_or(invalid_data_err(format!("Cannot find registrar symbol in file {:?}", file)))?;
105 let lib = load_library(file).map_err(invalid_data_err)?;
106 let exported_macros = {
107 let macros: libloading::Symbol<&&[bridge::client::ProcMacro]> =
108 unsafe { lib.get(symbol_name.as_bytes()) }.map_err(invalid_data_err)?;
112 Ok(ProcMacroLibraryLibloading { _lib: lib, exported_macros })
116 type ProcMacroLibraryImpl = ProcMacroLibraryLibloading;
118 pub struct Expander {
119 libs: Vec<ProcMacroLibraryImpl>,
123 pub fn new<P: AsRef<Path>>(lib: &P) -> Result<Expander, String> {
124 let mut libs = vec![];
125 /* Some libraries for dynamic loading require canonicalized path (even when it is
129 lib.as_ref().canonicalize().expect(&format!("Cannot canonicalize {:?}", lib.as_ref()));
131 let library = ProcMacroLibraryImpl::open(&lib).map_err(|e| e.to_string())?;
134 Ok(Expander { libs })
140 macro_body: &ra_tt::Subtree,
141 attributes: Option<&ra_tt::Subtree>,
142 ) -> Result<ra_tt::Subtree, bridge::PanicMessage> {
143 let parsed_body = TokenStream::with_subtree(macro_body.clone());
145 let parsed_attributes = attributes
146 .map_or(crate::rustc_server::TokenStream::new(), |attr| {
147 TokenStream::with_subtree(attr.clone())
150 for lib in &self.libs {
151 for proc_macro in &lib.exported_macros {
153 bridge::client::ProcMacro::CustomDerive { trait_name, client, .. }
154 if *trait_name == macro_name =>
156 let res = client.run(
157 &crate::proc_macro::bridge::server::SameThread,
158 crate::rustc_server::Rustc::default(),
161 return res.map(|it| it.subtree);
163 bridge::client::ProcMacro::Bang { name, client } if *name == macro_name => {
164 let res = client.run(
165 &crate::proc_macro::bridge::server::SameThread,
166 crate::rustc_server::Rustc::default(),
169 return res.map(|it| it.subtree);
171 bridge::client::ProcMacro::Attr { name, client } if *name == macro_name => {
172 let res = client.run(
173 &crate::proc_macro::bridge::server::SameThread,
174 crate::rustc_server::Rustc::default(),
179 return res.map(|it| it.subtree);
186 Err(bridge::PanicMessage::String("Nothing to expand".to_string()))
189 pub fn list_macros(&self) -> Result<Vec<(String, ProcMacroKind)>, bridge::PanicMessage> {
190 let mut result = vec![];
192 for lib in &self.libs {
193 for proc_macro in &lib.exported_macros {
194 let res = match proc_macro {
195 bridge::client::ProcMacro::CustomDerive { trait_name, .. } => {
196 (trait_name.to_string(), ProcMacroKind::CustomDerive)
198 bridge::client::ProcMacro::Bang { name, .. } => {
199 (name.to_string(), ProcMacroKind::FuncLike)
201 bridge::client::ProcMacro::Attr { name, .. } => {
202 (name.to_string(), ProcMacroKind::Attr)