2018-10-23 14:44:41 -07:00
|
|
|
//! Native loader
|
2020-04-08 14:36:18 -07:00
|
|
|
use crate::message_processor::SymbolCache;
|
2018-10-04 09:44:44 -07:00
|
|
|
#[cfg(unix)]
|
|
|
|
use libloading::os::unix::*;
|
|
|
|
#[cfg(windows)]
|
|
|
|
use libloading::os::windows::*;
|
2018-12-14 20:39:10 -08:00
|
|
|
use log::*;
|
2020-02-14 13:58:48 -08:00
|
|
|
use num_derive::{FromPrimitive, ToPrimitive};
|
2020-01-28 17:03:20 -08:00
|
|
|
use solana_sdk::{
|
2020-03-04 17:10:22 -08:00
|
|
|
account::KeyedAccount,
|
2020-04-08 14:36:18 -07:00
|
|
|
entrypoint_native,
|
2020-03-04 17:10:22 -08:00
|
|
|
instruction::InstructionError,
|
|
|
|
program_utils::{next_keyed_account, DecodeError},
|
|
|
|
pubkey::Pubkey,
|
2020-01-28 17:03:20 -08:00
|
|
|
};
|
2020-04-08 14:36:18 -07:00
|
|
|
use std::{env, path::PathBuf, str};
|
2020-02-14 13:58:48 -08:00
|
|
|
use thiserror::Error;
|
|
|
|
|
|
|
|
#[derive(Error, Debug, Serialize, Clone, PartialEq, FromPrimitive, ToPrimitive)]
|
|
|
|
pub enum NativeLoaderError {
|
|
|
|
#[error("Entrypoint name in the account data is not a valid UTF-8 string")]
|
2020-04-08 14:36:18 -07:00
|
|
|
InvalidEntrypointName,
|
2020-02-14 13:58:48 -08:00
|
|
|
#[error("Entrypoint was not found in the module")]
|
2020-04-08 14:36:18 -07:00
|
|
|
EntrypointNotFound,
|
2020-02-14 13:58:48 -08:00
|
|
|
#[error("Failed to load the module")]
|
2020-04-08 14:36:18 -07:00
|
|
|
FailedToLoad,
|
2020-02-14 13:58:48 -08:00
|
|
|
}
|
|
|
|
impl<T> DecodeError<T> for NativeLoaderError {
|
|
|
|
fn type_of() -> &'static str {
|
|
|
|
"NativeLoaderError"
|
|
|
|
}
|
|
|
|
}
|
2018-09-23 22:13:44 -07:00
|
|
|
|
2018-12-10 09:31:17 -08:00
|
|
|
/// Dynamic link library prefixes
|
2018-09-23 22:13:44 -07:00
|
|
|
#[cfg(unix)]
|
2020-04-08 14:36:18 -07:00
|
|
|
const PLATFORM_FILE_PREFIX_NATIVE: &str = "lib";
|
2018-09-23 22:13:44 -07:00
|
|
|
#[cfg(windows)]
|
2020-04-08 14:36:18 -07:00
|
|
|
const PLATFORM_FILE_PREFIX_NATIVE: &str = "";
|
2018-10-04 09:44:44 -07:00
|
|
|
|
2018-09-23 22:13:44 -07:00
|
|
|
/// Dynamic link library file extension specific to the platform
|
|
|
|
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
2020-04-08 14:36:18 -07:00
|
|
|
const PLATFORM_FILE_EXTENSION_NATIVE: &str = "dylib";
|
2018-09-23 22:13:44 -07:00
|
|
|
/// Dynamic link library file extension specific to the platform
|
|
|
|
#[cfg(all(unix, not(any(target_os = "macos", target_os = "ios"))))]
|
2020-04-08 14:36:18 -07:00
|
|
|
const PLATFORM_FILE_EXTENSION_NATIVE: &str = "so";
|
2018-09-23 22:13:44 -07:00
|
|
|
/// Dynamic link library file extension specific to the platform
|
|
|
|
#[cfg(windows)]
|
2020-04-08 14:36:18 -07:00
|
|
|
const PLATFORM_FILE_EXTENSION_NATIVE: &str = "dll";
|
2018-10-04 09:44:44 -07:00
|
|
|
|
2020-04-08 14:36:18 -07:00
|
|
|
fn create_path(name: &str) -> PathBuf {
|
|
|
|
let current_exe = env::current_exe()
|
|
|
|
.unwrap_or_else(|e| panic!("create_path(\"{}\"): current exe not found: {:?}", name, e));
|
|
|
|
let current_exe_directory = PathBuf::from(current_exe.parent().unwrap_or_else(|| {
|
|
|
|
panic!(
|
|
|
|
"create_path(\"{}\"): no parent directory of {:?}",
|
|
|
|
name, current_exe,
|
|
|
|
)
|
|
|
|
}));
|
2019-05-21 21:34:51 -07:00
|
|
|
|
2020-04-08 14:36:18 -07:00
|
|
|
let library_file_name = PathBuf::from(PLATFORM_FILE_PREFIX_NATIVE.to_string() + name)
|
|
|
|
.with_extension(PLATFORM_FILE_EXTENSION_NATIVE);
|
2018-10-04 09:44:44 -07:00
|
|
|
|
2020-04-08 14:36:18 -07:00
|
|
|
// Check the current_exe directory for the library as `cargo tests` are run
|
|
|
|
// from the deps/ subdirectory
|
|
|
|
let file_path = current_exe_directory.join(&library_file_name);
|
|
|
|
if file_path.exists() {
|
|
|
|
file_path
|
|
|
|
} else {
|
|
|
|
// `cargo build` places dependent libraries in the deps/ subdirectory
|
|
|
|
current_exe_directory.join("deps").join(library_file_name)
|
2018-12-08 10:43:02 -08:00
|
|
|
}
|
2020-04-08 14:36:18 -07:00
|
|
|
}
|
2018-10-04 09:44:44 -07:00
|
|
|
|
2020-04-08 14:36:18 -07:00
|
|
|
#[cfg(windows)]
|
|
|
|
fn library_open(path: &PathBuf) -> std::io::Result<Library> {
|
|
|
|
Library::new(path)
|
|
|
|
}
|
2019-06-04 21:49:05 -07:00
|
|
|
|
2020-04-08 14:36:18 -07:00
|
|
|
#[cfg(not(windows))]
|
|
|
|
fn library_open(path: &PathBuf) -> std::io::Result<Library> {
|
|
|
|
// Linux tls bug can cause crash on dlclose(), workaround by never unloading
|
|
|
|
Library::open(Some(path), libc::RTLD_NODELETE | libc::RTLD_NOW)
|
|
|
|
}
|
2019-06-04 21:49:05 -07:00
|
|
|
|
2020-04-08 14:36:18 -07:00
|
|
|
pub fn invoke_entrypoint(
|
|
|
|
_program_id: &Pubkey,
|
|
|
|
keyed_accounts: &[KeyedAccount],
|
|
|
|
instruction_data: &[u8],
|
|
|
|
symbol_cache: &SymbolCache,
|
|
|
|
) -> Result<(), InstructionError> {
|
|
|
|
let mut keyed_accounts_iter = keyed_accounts.iter();
|
|
|
|
let program = next_keyed_account(&mut keyed_accounts_iter)?;
|
|
|
|
let params = keyed_accounts_iter.as_slice();
|
|
|
|
let name_vec = &program.try_account_ref()?.data;
|
|
|
|
if let Some(entrypoint) = symbol_cache.read().unwrap().get(name_vec) {
|
|
|
|
unsafe {
|
|
|
|
return entrypoint(program.unsigned_key(), params, instruction_data);
|
2019-04-23 17:25:03 -07:00
|
|
|
}
|
2019-07-16 10:45:32 -06:00
|
|
|
}
|
2020-04-08 14:36:18 -07:00
|
|
|
let name = match str::from_utf8(name_vec) {
|
|
|
|
Ok(v) => v,
|
|
|
|
Err(e) => {
|
|
|
|
warn!("Invalid UTF-8 sequence: {}", e);
|
|
|
|
return Err(NativeLoaderError::InvalidEntrypointName.into());
|
|
|
|
}
|
|
|
|
};
|
|
|
|
trace!("Call native {:?}", name);
|
|
|
|
let path = create_path(&name);
|
|
|
|
match library_open(&path) {
|
|
|
|
Ok(library) => unsafe {
|
|
|
|
let entrypoint: Symbol<entrypoint_native::Entrypoint> =
|
|
|
|
match library.get(name.as_bytes()) {
|
|
|
|
Ok(s) => s,
|
|
|
|
Err(e) => {
|
|
|
|
warn!(
|
|
|
|
"Unable to find entrypoint {:?} (error: {:?})",
|
|
|
|
name.as_bytes(),
|
|
|
|
e
|
|
|
|
);
|
|
|
|
return Err(NativeLoaderError::EntrypointNotFound.into());
|
|
|
|
}
|
|
|
|
};
|
|
|
|
let ret = entrypoint(program.unsigned_key(), params, instruction_data);
|
|
|
|
symbol_cache
|
|
|
|
.write()
|
|
|
|
.unwrap()
|
|
|
|
.insert(name_vec.to_vec(), entrypoint);
|
|
|
|
ret
|
|
|
|
},
|
|
|
|
Err(e) => {
|
|
|
|
warn!("Failed to load: {:?}", e);
|
|
|
|
Err(NativeLoaderError::FailedToLoad.into())
|
2018-10-04 09:44:44 -07:00
|
|
|
}
|
|
|
|
}
|
2018-09-23 22:13:44 -07:00
|
|
|
}
|