diff --git a/cli/src/program.rs b/cli/src/program.rs index 3d5f72af0a..70bf00ba8e 100644 --- a/cli/src/program.rs +++ b/cli/src/program.rs @@ -10,7 +10,7 @@ use { clap::{App, AppSettings, Arg, ArgMatches, SubCommand}, log::*, solana_account_decoder::{UiAccountEncoding, UiDataSliceConfig}, - solana_bpf_loader_program::syscalls::create_loader, + solana_bpf_loader_program::syscalls::create_program_runtime_environment, solana_clap_utils::{ self, hidden_unless_forced, input_parsers::*, input_validators::*, keypair::*, }, @@ -2022,16 +2022,18 @@ fn read_and_verify_elf(program_location: &str) -> Result, Box::from_elf(&program_data, loader) - .map_err(|err| format!("ELF error: {err}"))?; + let executable = Executable::::from_elf( + &program_data, + Arc::new(program_runtime_environment), + ) + .map_err(|err| format!("ELF error: {err}"))?; let _ = Executable::::verified(executable) .map_err(|err| format!("ELF error: {err}"))?; diff --git a/ledger-tool/src/program.rs b/ledger-tool/src/program.rs index 8488b52d24..26234373d2 100644 --- a/ledger-tool/src/program.rs +++ b/ledger-tool/src/program.rs @@ -6,7 +6,7 @@ use { serde_json::Result, solana_bpf_loader_program::{ create_vm, load_program_from_bytes, serialization::serialize_parameters, - syscalls::create_loader, + syscalls::create_program_runtime_environment, }, solana_clap_utils::input_parsers::pubkeys_of, solana_ledger::{ @@ -341,8 +341,6 @@ fn load_program<'a>( let mut contents = Vec::new(); file.read_to_end(&mut contents).unwrap(); let slot = Slot::default(); - let reject_deployment_of_broken_elfs = false; - let debugging_features = true; let log_collector = invoke_context.get_log_collector(); let loader_key = bpf_loader_upgradeable::id(); let mut load_program_metrics = LoadProgramMetrics { @@ -350,46 +348,51 @@ fn load_program<'a>( ..LoadProgramMetrics::default() }; let account_size = contents.len(); + let program_runtime_environment = create_program_runtime_environment( + &invoke_context.feature_set, + invoke_context.get_compute_budget(), + false, /* deployment */ + true, /* debugging_features */ + ) + .unwrap(); // Allowing mut here, since it may be needed for jit compile, which is under a config flag #[allow(unused_mut)] let mut verified_executable = if is_elf { let result = load_program_from_bytes( &invoke_context.feature_set, - invoke_context.get_compute_budget(), log_collector, &mut load_program_metrics, &contents, &loader_key, account_size, slot, - reject_deployment_of_broken_elfs, - debugging_features, + Arc::new(program_runtime_environment), ); match result { Ok(loaded_program) => match loaded_program.program { - LoadedProgramType::LegacyV1(program) => Ok(unsafe { std::mem::transmute(program) }), + LoadedProgramType::LegacyV1(program) => Ok(program), _ => unreachable!(), }, Err(err) => Err(format!("Loading executable failed: {err:?}")), } } else { - let loader = create_loader( - &invoke_context.feature_set, - invoke_context.get_compute_budget(), - true, - true, + let executable = assemble::( + std::str::from_utf8(contents.as_slice()).unwrap(), + Arc::new(program_runtime_environment), ) .unwrap(); - let executable = - assemble::(std::str::from_utf8(contents.as_slice()).unwrap(), loader) - .unwrap(); Executable::::verified(executable) .map_err(|err| format!("Assembling executable failed: {err:?}")) } .unwrap(); #[cfg(all(not(target_os = "windows"), target_arch = "x86_64"))] verified_executable.jit_compile().unwrap(); - verified_executable + unsafe { + std::mem::transmute::< + Executable>, + Executable>, + >(verified_executable) + } } enum Action { @@ -537,7 +540,7 @@ pub fn program(ledger_path: &Path, matches: &ArgMatches<'_>) { let mut loaded_programs = LoadedProgramsForTxBatch::new(bank.slot() + DELAY_VISIBILITY_SLOT_OFFSET); for key in cached_account_keys { - let program = bank.load_program(&key, true).unwrap_or_else(|err| { + let program = bank.load_program(&key).unwrap_or_else(|err| { // Create a tombstone for the program in the cache debug!("Failed to load program {}, error {:?}", key, err); Arc::new(LoadedProgram::new_tombstone( diff --git a/program-runtime/src/loaded_programs.rs b/program-runtime/src/loaded_programs.rs index 2f3496a03a..829bb711e0 100644 --- a/program-runtime/src/loaded_programs.rs +++ b/program-runtime/src/loaded_programs.rs @@ -204,7 +204,7 @@ impl LoadedProgram { /// Creates a new user program pub fn new( loader_key: &Pubkey, - loader: Arc>>, + program_runtime_environment: Arc>>, deployment_slot: Slot, effective_slot: Slot, maybe_expiration_slot: Option, @@ -213,7 +213,7 @@ impl LoadedProgram { metrics: &mut LoadProgramMetrics, ) -> Result> { let mut load_elf_time = Measure::start("load_elf_time"); - let executable = Executable::load(elf_bytes, loader.clone())?; + let executable = Executable::load(elf_bytes, program_runtime_environment.clone())?; load_elf_time.stop(); metrics.load_elf_us = load_elf_time.as_us(); @@ -337,7 +337,8 @@ pub struct LoadedPrograms { /// /// Pubkey is the address of a program, multiple versions can coexists simultaneously under the same address (in different slots). entries: HashMap>>, - + /// Globally shared RBPF config and syscall registry + pub program_runtime_environment_v1: Arc>>, latest_root: Slot, pub stats: Stats, } diff --git a/programs/bpf_loader/src/lib.rs b/programs/bpf_loader/src/lib.rs index 3da64cc224..822753f81b 100644 --- a/programs/bpf_loader/src/lib.rs +++ b/programs/bpf_loader/src/lib.rs @@ -7,7 +7,6 @@ pub mod syscalls; use { solana_measure::measure::Measure, solana_program_runtime::{ - compute_budget::ComputeBudget, ic_logger_msg, ic_msg, invoke_context::{BpfAllocator, InvokeContext, SyscallContext}, loaded_programs::{ @@ -24,7 +23,7 @@ use { error::EbpfError, memory_region::{AccessType, MemoryCowCallback, MemoryMapping, MemoryRegion}, verifier::RequisiteVerifier, - vm::{ContextObject, EbpfVm, ProgramResult}, + vm::{BuiltInProgram, ContextObject, EbpfVm, ProgramResult}, }, solana_sdk::{ account::WritableAccount, @@ -60,34 +59,20 @@ use { rc::Rc, sync::{atomic::Ordering, Arc}, }, + syscalls::create_program_runtime_environment, }; #[allow(clippy::too_many_arguments)] pub fn load_program_from_bytes( feature_set: &FeatureSet, - compute_budget: &ComputeBudget, log_collector: Option>>, load_program_metrics: &mut LoadProgramMetrics, programdata: &[u8], loader_key: &Pubkey, account_size: usize, deployment_slot: Slot, - reject_deployment_of_broken_elfs: bool, - debugging_features: bool, + program_runtime_environment: Arc>>, ) -> Result { - let mut register_syscalls_time = Measure::start("register_syscalls_time"); - let loader = syscalls::create_loader( - feature_set, - compute_budget, - reject_deployment_of_broken_elfs, - debugging_features, - ) - .map_err(|e| { - ic_logger_msg!(log_collector, "Failed to register syscalls: {}", e); - InstructionError::ProgramEnvironmentSetupFailure - })?; - register_syscalls_time.stop(); - load_program_metrics.register_syscalls_us = register_syscalls_time.as_us(); let effective_slot = if feature_set.is_active(&delay_visibility_of_program_deployment::id()) { deployment_slot.saturating_add(DELAY_VISIBILITY_SLOT_OFFSET) } else { @@ -95,7 +80,7 @@ pub fn load_program_from_bytes( }; let loaded_program = LoadedProgram::new( loader_key, - loader, + program_runtime_environment, deployment_slot, effective_slot, None, @@ -110,42 +95,12 @@ pub fn load_program_from_bytes( Ok(loaded_program) } -fn get_programdata_offset_and_deployment_offset( - log_collector: &Option>>, - program: &BorrowedAccount, - programdata: &BorrowedAccount, -) -> Result<(usize, Slot), InstructionError> { - if bpf_loader_upgradeable::check_id(program.get_owner()) { - if let UpgradeableLoaderState::Program { - programdata_address: _, - } = program.get_state()? - { - if let UpgradeableLoaderState::ProgramData { - slot, - upgrade_authority_address: _, - } = programdata.get_state()? - { - Ok((UpgradeableLoaderState::size_of_programdata_metadata(), slot)) - } else { - ic_logger_msg!(log_collector, "Program has been closed"); - Err(InstructionError::InvalidAccountData) - } - } else { - ic_logger_msg!(log_collector, "Invalid Program account"); - Err(InstructionError::InvalidAccountData) - } - } else { - Ok((0, 0)) - } -} - pub fn load_program_from_account( feature_set: &FeatureSet, - compute_budget: &ComputeBudget, log_collector: Option>>, program: &BorrowedAccount, programdata: &BorrowedAccount, - debugging_features: bool, + program_runtime_environment: Arc>>, ) -> Result<(Arc, LoadProgramMetrics), InstructionError> { if !check_loader_id(program.get_owner()) { ic_logger_msg!( @@ -156,7 +111,28 @@ pub fn load_program_from_account( } let (programdata_offset, deployment_slot) = - get_programdata_offset_and_deployment_offset(&log_collector, program, programdata)?; + if bpf_loader_upgradeable::check_id(program.get_owner()) { + if let UpgradeableLoaderState::Program { + programdata_address: _, + } = program.get_state()? + { + if let UpgradeableLoaderState::ProgramData { + slot, + upgrade_authority_address: _, + } = programdata.get_state()? + { + (UpgradeableLoaderState::size_of_programdata_metadata(), slot) + } else { + ic_logger_msg!(log_collector, "Program has been closed"); + return Err(InstructionError::InvalidAccountData); + } + } else { + ic_logger_msg!(log_collector, "Invalid Program account"); + return Err(InstructionError::InvalidAccountData); + } + } else { + (0, 0) + }; let programdata_size = if programdata_offset != 0 { programdata.get_data().len() @@ -171,7 +147,6 @@ pub fn load_program_from_account( let loaded_program = Arc::new(load_program_from_bytes( feature_set, - compute_budget, log_collector, &mut load_program_metrics, programdata @@ -181,8 +156,7 @@ pub fn load_program_from_account( program.get_owner(), program.get_data().len().saturating_add(programdata_size), deployment_slot, - false, /* reject_deployment_of_broken_elfs */ - debugging_features, + program_runtime_environment, )?); Ok((loaded_program, load_program_metrics)) @@ -204,17 +178,27 @@ macro_rules! deploy_program { ($invoke_context:expr, $program_id:expr, $loader_key:expr, $account_size:expr, $slot:expr, $drop:expr, $new_programdata:expr $(,)?) => {{ let mut load_program_metrics = LoadProgramMetrics::default(); - let executor = load_program_from_bytes( + let mut register_syscalls_time = Measure::start("register_syscalls_time"); + let program_runtime_environment = create_program_runtime_environment( &$invoke_context.feature_set, $invoke_context.get_compute_budget(), + true, /* deployment */ + false, /* debugging_features */ + ).map_err(|e| { + ic_msg!($invoke_context, "Failed to register syscalls: {}", e); + InstructionError::ProgramEnvironmentSetupFailure + })?; + register_syscalls_time.stop(); + load_program_metrics.register_syscalls_us = register_syscalls_time.as_us(); + let executor = load_program_from_bytes( + &$invoke_context.feature_set, $invoke_context.get_log_collector(), &mut load_program_metrics, $new_programdata, $loader_key, $account_size, $slot, - true, /* reject_deployment_of_broken_elfs */ - false, /* debugging_features */ + Arc::new(program_runtime_environment), )?; if let Some(old_entry) = find_program_in_cache($invoke_context, &$program_id) { let usage_counter = old_entry.usage_counter.load(Ordering::Relaxed); @@ -1675,6 +1659,14 @@ pub mod test_utils { }; pub fn load_all_invoked_programs(invoke_context: &mut InvokeContext) { + let mut load_program_metrics = LoadProgramMetrics::default(); + let program_runtime_environment = create_program_runtime_environment( + &invoke_context.feature_set, + invoke_context.get_compute_budget(), + false, /* deployment */ + false, /* debugging_features */ + ); + let program_runtime_environment = Arc::new(program_runtime_environment.unwrap()); let num_accounts = invoke_context.transaction_context.get_number_of_accounts(); for index in 0..num_accounts { let account = invoke_context @@ -1690,19 +1682,15 @@ pub mod test_utils { .get_key_of_account_at_index(index) .expect("Failed to get account key"); - let mut load_program_metrics = LoadProgramMetrics::default(); - if let Ok(loaded_program) = load_program_from_bytes( &FeatureSet::all_enabled(), - &ComputeBudget::default(), None, &mut load_program_metrics, account.data(), owner, account.data().len(), 0, - true, - false, + program_runtime_environment.clone(), ) { invoke_context .programs_modified_by_tx diff --git a/programs/bpf_loader/src/syscalls/mod.rs b/programs/bpf_loader/src/syscalls/mod.rs index 3132cd9770..449668a3a1 100644 --- a/programs/bpf_loader/src/syscalls/mod.rs +++ b/programs/bpf_loader/src/syscalls/mod.rs @@ -142,12 +142,12 @@ macro_rules! register_feature_gated_function { }; } -pub fn create_loader<'a>( +pub fn create_program_runtime_environment<'a>( feature_set: &FeatureSet, compute_budget: &ComputeBudget, reject_deployment_of_broken_elfs: bool, debugging_features: bool, -) -> Result>>, Error> { +) -> Result>, Error> { use rand::Rng; let config = Config { max_call_depth: compute_budget.max_call_depth, @@ -312,7 +312,7 @@ pub fn create_loader<'a>( // Log data result.register_function(b"sol_log_data", SyscallLogData::call)?; - Ok(Arc::new(result)) + Ok(result) } fn translate( diff --git a/programs/sbf/benches/bpf_loader.rs b/programs/sbf/benches/bpf_loader.rs index c9600372cf..c37082eb0f 100644 --- a/programs/sbf/benches/bpf_loader.rs +++ b/programs/sbf/benches/bpf_loader.rs @@ -13,7 +13,8 @@ extern crate test; use { byteorder::{ByteOrder, LittleEndian, WriteBytesExt}, solana_bpf_loader_program::{ - create_vm, serialization::serialize_parameters, syscalls::create_loader, + create_vm, serialization::serialize_parameters, + syscalls::create_program_runtime_environment, }, solana_measure::measure::Measure, solana_program_runtime::{compute_budget::ComputeBudget, invoke_context::InvokeContext}, @@ -89,16 +90,19 @@ macro_rules! with_mock_invoke_context { fn bench_program_create_executable(bencher: &mut Bencher) { let elf = load_program_from_file("bench_alu"); - let loader = create_loader( + let program_runtime_environment = create_program_runtime_environment( &FeatureSet::default(), &ComputeBudget::default(), true, false, - ) - .unwrap(); + ); + let program_runtime_environment = Arc::new(program_runtime_environment.unwrap()); bencher.iter(|| { - let _ = - Executable::::from_elf(&elf, loader.clone()).unwrap(); + let _ = Executable::::from_elf( + &elf, + program_runtime_environment.clone(), + ) + .unwrap(); }); } @@ -114,15 +118,17 @@ fn bench_program_alu(bencher: &mut Bencher) { let elf = load_program_from_file("bench_alu"); with_mock_invoke_context!(invoke_context, bpf_loader::id(), 10000001); - let loader = create_loader( + let program_runtime_environment = create_program_runtime_environment( &invoke_context.feature_set, &ComputeBudget::default(), true, false, + ); + let executable = Executable::::from_elf( + &elf, + Arc::new(program_runtime_environment.unwrap()), ) .unwrap(); - let executable = - Executable::::from_elf(&elf, loader).unwrap(); let mut verified_executable = Executable::::verified(executable).unwrap(); @@ -231,15 +237,17 @@ fn bench_create_vm(bencher: &mut Bencher) { let direct_mapping = invoke_context .feature_set .is_active(&bpf_account_data_direct_mapping::id()); - let loader = create_loader( + let program_runtime_environment = create_program_runtime_environment( &invoke_context.feature_set, &ComputeBudget::default(), true, false, + ); + let executable = Executable::::from_elf( + &elf, + Arc::new(program_runtime_environment.unwrap()), ) .unwrap(); - let executable = - Executable::::from_elf(&elf, loader).unwrap(); let verified_executable = Executable::::verified(executable).unwrap(); @@ -291,15 +299,17 @@ fn bench_instruction_count_tuner(_bencher: &mut Bencher) { ) .unwrap(); - let loader = create_loader( + let program_runtime_environment = create_program_runtime_environment( &invoke_context.feature_set, &ComputeBudget::default(), true, false, + ); + let executable = Executable::::from_elf( + &elf, + Arc::new(program_runtime_environment.unwrap()), ) .unwrap(); - let executable = - Executable::::from_elf(&elf, loader).unwrap(); let verified_executable = Executable::::verified(executable).unwrap(); diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index 34b978c294..0854575bf7 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -89,6 +89,7 @@ use { iter::{IntoParallelIterator, IntoParallelRefIterator, ParallelIterator}, ThreadPool, ThreadPoolBuilder, }, + solana_bpf_loader_program::syscalls::create_program_runtime_environment, solana_measure::{measure, measure::Measure, measure_us}, solana_perf::perf_libs, solana_program_runtime::{ @@ -4117,11 +4118,7 @@ impl Bank { } } - pub fn load_program( - &self, - pubkey: &Pubkey, - debugging_features: bool, - ) -> Result> { + pub fn load_program(&self, pubkey: &Pubkey) -> Result> { let program = if let Some(program) = self.get_account_with_fixed_root(pubkey) { program } else { @@ -4174,13 +4171,18 @@ impl Bank { } else { None }; + let program_runtime_environment_v1 = self + .loaded_programs_cache + .read() + .unwrap() + .program_runtime_environment_v1 + .clone(); solana_bpf_loader_program::load_program_from_account( &self.feature_set, - &self.runtime_config.compute_budget.unwrap_or_default(), None, // log_collector &program, programdata.as_ref().unwrap_or(&program), - debugging_features, + program_runtime_environment_v1, ) .map(|(loaded_program, metrics)| { let mut timings = ExecuteDetailsTimings::default(); @@ -4428,7 +4430,7 @@ impl Bank { let missing_programs: Vec<(Pubkey, Arc)> = missing_programs .iter() .map(|key| { - let program = self.load_program(key, false).unwrap_or_else(|err| { + let program = self.load_program(key).unwrap_or_else(|err| { // Create a tombstone for the program in the cache debug!("Failed to load program {}, error {:?}", key, err); Arc::new(LoadedProgram::new_tombstone( @@ -4525,9 +4527,6 @@ impl Bank { &program_owners_refs, &self.blockhash_queue.read().unwrap(), ); - for precompile in get_precompiles() { - program_accounts_map.remove(&precompile.program_id); - } let native_loader = native_loader::id(); for builtin_program in self.builtin_programs.iter() { program_accounts_map.insert(*builtin_program, &native_loader); @@ -6265,7 +6264,23 @@ impl Bank { self.rewards_pool_pubkeys = Arc::new(genesis_config.rewards_pools.keys().cloned().collect()); + self.apply_feature_activations( + ApplyFeatureActivationsCaller::FinishInit, + debug_do_not_add_builtins, + ); + if !debug_do_not_add_builtins { + let program_runtime_environment_v1 = create_program_runtime_environment( + &self.feature_set, + &self.runtime_config.compute_budget.unwrap_or_default(), + false, /* deployment */ + false, /* debugging_features */ + ) + .unwrap(); + self.loaded_programs_cache + .write() + .unwrap() + .program_runtime_environment_v1 = Arc::new(program_runtime_environment_v1); for builtin in BUILTINS .iter() .chain(additional_builtins.unwrap_or(&[]).iter()) @@ -6285,11 +6300,6 @@ impl Bank { } } - self.apply_feature_activations( - ApplyFeatureActivationsCaller::FinishInit, - debug_do_not_add_builtins, - ); - if self .feature_set .is_active(&feature_set::cap_accounts_data_len::id()) diff --git a/runtime/src/bank/tests.rs b/runtime/src/bank/tests.rs index 26606dcb12..59fad31f23 100644 --- a/runtime/src/bank/tests.rs +++ b/runtime/src/bank/tests.rs @@ -7397,7 +7397,7 @@ fn test_bank_load_program() { programdata_account.set_rent_epoch(1); bank.store_account_and_update_capitalization(&key1, &program_account); bank.store_account_and_update_capitalization(&programdata_key, &programdata_account); - let program = bank.load_program(&key1, false); + let program = bank.load_program(&key1); assert!(program.is_ok()); let program = program.unwrap(); assert!(matches!(program.program, LoadedProgramType::LegacyV1(_))); @@ -7555,7 +7555,7 @@ fn test_bpf_loader_upgradeable_deploy_with_max_len() { } let loaded_program = bank - .load_program(&program_keypair.pubkey(), false) + .load_program(&program_keypair.pubkey()) .expect("Failed to load the program"); // Invoke deployed program