Refactor - Syscalls in RBPF CLI (#31109)
* Makes members of SyscallContext public. Removes check_aligned and check_size from SyscallContext. * Replaces InvokeContext::set_syscall_context() in tests with mock_create_vm!(). * Passes SyscallContext directly to InvokeContext::set_syscall_context(). * Merges TraceLogStackFrame into SyscallContext. * Removes the create_vm!() macro. * Moves BpfAllocator from bpf_loader into program_runtime. * Frees BpfAllocator from Rc<RefCell<>>. * Removes unused code from BpfAllocator. * Consume CUs for heap before doing the allocation. * Exposes syscalls in rbpf-cli. * Adds debugging_features parameter to load_program_from_bytes() and load_program_from_account(). * Removes test_bpf_loader_non_terminating_program() as that is already tested in RBPF. * Moves stack and heap allocation back onto the program runtime stack.
This commit is contained in:
parent
592b3ce17e
commit
8eb31f6cfd
|
@ -11,12 +11,13 @@ use {
|
|||
timings::{ExecuteDetailsTimings, ExecuteTimings},
|
||||
},
|
||||
solana_measure::measure::Measure,
|
||||
solana_rbpf::vm::ContextObject,
|
||||
solana_rbpf::{ebpf::MM_HEAP_START, vm::ContextObject},
|
||||
solana_sdk::{
|
||||
account::{AccountSharedData, ReadableAccount},
|
||||
bpf_loader_deprecated,
|
||||
feature_set::{
|
||||
enable_early_verification_of_account_modifications, native_programs_consume_cu,
|
||||
FeatureSet,
|
||||
check_slice_translation_size, enable_early_verification_of_account_modifications,
|
||||
native_programs_consume_cu, FeatureSet,
|
||||
},
|
||||
hash::Hash,
|
||||
instruction::{AccountMeta, InstructionError},
|
||||
|
@ -87,15 +88,16 @@ impl std::fmt::Debug for BuiltinProgram {
|
|||
|
||||
impl<'a> ContextObject for InvokeContext<'a> {
|
||||
fn trace(&mut self, state: [u64; 12]) {
|
||||
self.trace_log_stack
|
||||
self.syscall_context
|
||||
.last_mut()
|
||||
.expect("Inconsistent trace log stack")
|
||||
.unwrap()
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.trace_log
|
||||
.push(state);
|
||||
}
|
||||
|
||||
fn consume(&mut self, amount: u64) {
|
||||
self.log_consumed_bpf_units(amount);
|
||||
// 1 to 1 instruction to compute unit mapping
|
||||
// ignore overflow, Ebpf will bail if exceeded
|
||||
let mut compute_meter = self.compute_meter.borrow_mut();
|
||||
|
@ -107,32 +109,46 @@ impl<'a> ContextObject for InvokeContext<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Based loosely on the unstable std::alloc::Alloc trait
|
||||
pub trait Alloc {
|
||||
fn alloc(&mut self, layout: Layout) -> Result<u64, AllocErr>;
|
||||
fn dealloc(&mut self, addr: u64, layout: Layout);
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct AllocErr;
|
||||
|
||||
impl fmt::Display for AllocErr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.write_str("Error: Memory allocation failed")
|
||||
}
|
||||
}
|
||||
|
||||
struct SyscallContext {
|
||||
check_aligned: bool,
|
||||
check_size: bool,
|
||||
orig_account_lengths: Vec<usize>,
|
||||
allocator: Rc<RefCell<dyn Alloc>>,
|
||||
pub struct BpfAllocator {
|
||||
len: u64,
|
||||
pos: u64,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct TraceLogStackFrame {
|
||||
impl BpfAllocator {
|
||||
pub fn new(len: u64) -> Self {
|
||||
Self { len, pos: 0 }
|
||||
}
|
||||
|
||||
pub fn alloc(&mut self, layout: Layout) -> Result<u64, AllocErr> {
|
||||
let bytes_to_align = (self.pos as *const u8).align_offset(layout.align()) as u64;
|
||||
if self
|
||||
.pos
|
||||
.saturating_add(bytes_to_align)
|
||||
.saturating_add(layout.size() as u64)
|
||||
<= self.len
|
||||
{
|
||||
self.pos = self.pos.saturating_add(bytes_to_align);
|
||||
let addr = MM_HEAP_START.saturating_add(self.pos);
|
||||
self.pos = self.pos.saturating_add(layout.size() as u64);
|
||||
Ok(addr)
|
||||
} else {
|
||||
Err(AllocErr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SyscallContext {
|
||||
pub allocator: BpfAllocator,
|
||||
pub orig_account_lengths: Vec<usize>,
|
||||
pub trace_log: Vec<[u64; 12]>,
|
||||
pub consumed_bpf_units: RefCell<Vec<(usize, u64)>>,
|
||||
}
|
||||
|
||||
pub struct InvokeContext<'a> {
|
||||
|
@ -141,7 +157,6 @@ pub struct InvokeContext<'a> {
|
|||
pre_accounts: Vec<PreAccount>,
|
||||
builtin_programs: &'a [BuiltinProgram],
|
||||
sysvar_cache: &'a SysvarCache,
|
||||
pub trace_log_stack: Vec<TraceLogStackFrame>,
|
||||
log_collector: Option<Rc<RefCell<LogCollector>>>,
|
||||
compute_budget: ComputeBudget,
|
||||
current_compute_budget: ComputeBudget,
|
||||
|
@ -152,8 +167,7 @@ pub struct InvokeContext<'a> {
|
|||
pub timings: ExecuteDetailsTimings,
|
||||
pub blockhash: Hash,
|
||||
pub lamports_per_signature: u64,
|
||||
syscall_context: Vec<Option<SyscallContext>>,
|
||||
pub enable_instruction_tracing: bool,
|
||||
pub syscall_context: Vec<Option<SyscallContext>>,
|
||||
}
|
||||
|
||||
impl<'a> InvokeContext<'a> {
|
||||
|
@ -177,7 +191,6 @@ impl<'a> InvokeContext<'a> {
|
|||
pre_accounts: Vec::new(),
|
||||
builtin_programs,
|
||||
sysvar_cache,
|
||||
trace_log_stack: vec![TraceLogStackFrame::default()],
|
||||
log_collector,
|
||||
current_compute_budget: compute_budget,
|
||||
compute_budget,
|
||||
|
@ -189,7 +202,6 @@ impl<'a> InvokeContext<'a> {
|
|||
blockhash,
|
||||
lamports_per_signature,
|
||||
syscall_context: Vec::new(),
|
||||
enable_instruction_tracing: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -273,14 +285,12 @@ impl<'a> InvokeContext<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
self.trace_log_stack.push(TraceLogStackFrame::default());
|
||||
self.syscall_context.push(None);
|
||||
self.transaction_context.push()
|
||||
}
|
||||
|
||||
/// Pop a stack frame from the invocation stack
|
||||
pub fn pop(&mut self) -> Result<(), InstructionError> {
|
||||
self.trace_log_stack.pop();
|
||||
self.syscall_context.pop();
|
||||
self.transaction_context.pop()
|
||||
}
|
||||
|
@ -764,7 +774,6 @@ impl<'a> InvokeContext<'a> {
|
|||
|
||||
/// Consume compute units
|
||||
pub fn consume_checked(&self, amount: u64) -> Result<(), Box<dyn std::error::Error>> {
|
||||
self.log_consumed_bpf_units(amount);
|
||||
let mut compute_meter = self.compute_meter.borrow_mut();
|
||||
let exceeded = *compute_meter < amount;
|
||||
*compute_meter = compute_meter.saturating_sub(amount);
|
||||
|
@ -796,75 +805,53 @@ impl<'a> InvokeContext<'a> {
|
|||
self.sysvar_cache
|
||||
}
|
||||
|
||||
// Set this instruction syscall context
|
||||
pub fn set_syscall_context(
|
||||
&mut self,
|
||||
check_aligned: bool,
|
||||
check_size: bool,
|
||||
orig_account_lengths: Vec<usize>,
|
||||
allocator: Rc<RefCell<dyn Alloc>>,
|
||||
) -> Result<(), InstructionError> {
|
||||
*self
|
||||
.syscall_context
|
||||
.last_mut()
|
||||
.ok_or(InstructionError::CallDepth)? = Some(SyscallContext {
|
||||
check_aligned,
|
||||
check_size,
|
||||
orig_account_lengths,
|
||||
allocator,
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Should alignment be enforced during user pointer translation
|
||||
pub fn get_check_aligned(&self) -> bool {
|
||||
self.syscall_context
|
||||
.last()
|
||||
.and_then(|context| context.as_ref())
|
||||
.map(|context| context.check_aligned)
|
||||
self.transaction_context
|
||||
.get_current_instruction_context()
|
||||
.and_then(|instruction_context| {
|
||||
let program_account =
|
||||
instruction_context.try_borrow_last_program_account(self.transaction_context);
|
||||
debug_assert!(program_account.is_ok());
|
||||
program_account
|
||||
})
|
||||
.map(|program_account| *program_account.get_owner() != bpf_loader_deprecated::id())
|
||||
.unwrap_or(true)
|
||||
}
|
||||
|
||||
// Set should type size be checked during user pointer translation
|
||||
pub fn get_check_size(&self) -> bool {
|
||||
self.syscall_context
|
||||
.last()
|
||||
.and_then(|context| context.as_ref())
|
||||
.map(|context| context.check_size)
|
||||
.unwrap_or(true)
|
||||
self.feature_set
|
||||
.is_active(&check_slice_translation_size::id())
|
||||
}
|
||||
|
||||
/// Get the original account lengths
|
||||
pub fn get_orig_account_lengths(&self) -> Result<&[usize], InstructionError> {
|
||||
// Set this instruction syscall context
|
||||
pub fn set_syscall_context(
|
||||
&mut self,
|
||||
syscall_context: SyscallContext,
|
||||
) -> Result<(), InstructionError> {
|
||||
*self
|
||||
.syscall_context
|
||||
.last_mut()
|
||||
.ok_or(InstructionError::CallDepth)? = Some(syscall_context);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Get this instruction's SyscallContext
|
||||
pub fn get_syscall_context(&self) -> Result<&SyscallContext, InstructionError> {
|
||||
self.syscall_context
|
||||
.last()
|
||||
.and_then(|context| context.as_ref())
|
||||
.map(|context| context.orig_account_lengths.as_slice())
|
||||
.and_then(|syscall_context| syscall_context.as_ref())
|
||||
.ok_or(InstructionError::CallDepth)
|
||||
}
|
||||
|
||||
// Get this instruction's memory allocator
|
||||
pub fn get_allocator(&self) -> Result<Rc<RefCell<dyn Alloc>>, InstructionError> {
|
||||
// Get this instruction's SyscallContext
|
||||
pub fn get_syscall_context_mut(&mut self) -> Result<&mut SyscallContext, InstructionError> {
|
||||
self.syscall_context
|
||||
.last()
|
||||
.and_then(|context| context.as_ref())
|
||||
.map(|context| context.allocator.clone())
|
||||
.last_mut()
|
||||
.and_then(|syscall_context| syscall_context.as_mut())
|
||||
.ok_or(InstructionError::CallDepth)
|
||||
}
|
||||
|
||||
fn log_consumed_bpf_units(&self, amount: u64) {
|
||||
if self.enable_instruction_tracing && amount != 0 {
|
||||
let trace_log_stack_frame = self
|
||||
.trace_log_stack
|
||||
.last()
|
||||
.expect("Inconsistent trace log stack");
|
||||
|
||||
trace_log_stack_frame.consumed_bpf_units.borrow_mut().push((
|
||||
trace_log_stack_frame.trace_log.len().saturating_sub(1),
|
||||
amount,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
|
|
|
@ -1,55 +0,0 @@
|
|||
#![allow(clippy::integer_arithmetic)]
|
||||
|
||||
use {
|
||||
solana_program_runtime::invoke_context::{Alloc, AllocErr},
|
||||
solana_rbpf::{aligned_memory::AlignedMemory, ebpf::HOST_ALIGN},
|
||||
std::alloc::Layout,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BpfAllocator {
|
||||
#[allow(dead_code)]
|
||||
heap: AlignedMemory<HOST_ALIGN>,
|
||||
start: u64,
|
||||
len: u64,
|
||||
pos: u64,
|
||||
}
|
||||
|
||||
impl BpfAllocator {
|
||||
pub fn new(heap: AlignedMemory<HOST_ALIGN>, virtual_address: u64) -> Self {
|
||||
let len = heap.len() as u64;
|
||||
Self {
|
||||
heap,
|
||||
start: virtual_address,
|
||||
len,
|
||||
pos: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn heap_mut(&mut self) -> &mut AlignedMemory<HOST_ALIGN> {
|
||||
&mut self.heap
|
||||
}
|
||||
}
|
||||
|
||||
impl Alloc for BpfAllocator {
|
||||
fn alloc(&mut self, layout: Layout) -> Result<u64, AllocErr> {
|
||||
let bytes_to_align = (self.pos as *const u8).align_offset(layout.align()) as u64;
|
||||
if self
|
||||
.pos
|
||||
.saturating_add(layout.size() as u64)
|
||||
.saturating_add(bytes_to_align)
|
||||
<= self.len
|
||||
{
|
||||
self.pos += bytes_to_align;
|
||||
let addr = self.start + self.pos;
|
||||
self.pos += layout.size() as u64;
|
||||
Ok(addr)
|
||||
} else {
|
||||
Err(AllocErr)
|
||||
}
|
||||
}
|
||||
|
||||
fn dealloc(&mut self, _addr: u64, _layout: Layout) {
|
||||
// It's a bump allocator, free not supported
|
||||
}
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
#![deny(clippy::integer_arithmetic)]
|
||||
#![deny(clippy::indexing_slicing)]
|
||||
|
||||
pub mod allocator_bump;
|
||||
pub mod deprecated;
|
||||
pub mod serialization;
|
||||
pub mod syscalls;
|
||||
|
@ -10,13 +9,12 @@ pub mod upgradeable_with_jit;
|
|||
pub mod with_jit;
|
||||
|
||||
use {
|
||||
crate::allocator_bump::BpfAllocator,
|
||||
solana_measure::measure::Measure,
|
||||
solana_program_runtime::{
|
||||
compute_budget::ComputeBudget,
|
||||
executor_cache::TransactionExecutorCache,
|
||||
ic_logger_msg, ic_msg,
|
||||
invoke_context::InvokeContext,
|
||||
invoke_context::{BpfAllocator, InvokeContext, SyscallContext},
|
||||
loaded_programs::{LoadProgramMetrics, LoadedProgram, LoadedProgramType},
|
||||
log_collector::LogCollector,
|
||||
stable_log,
|
||||
|
@ -37,11 +35,10 @@ use {
|
|||
entrypoint::SUCCESS,
|
||||
feature_set::{
|
||||
cap_accounts_data_allocations_per_transaction, cap_bpf_program_instruction_accounts,
|
||||
check_slice_translation_size, delay_visibility_of_program_deployment,
|
||||
disable_deploy_of_alloc_free_syscall, enable_bpf_loader_extend_program_ix,
|
||||
enable_bpf_loader_set_authority_checked_ix, enable_program_redeployment_cooldown,
|
||||
limit_max_instruction_trace_length, native_programs_consume_cu,
|
||||
remove_bpf_loader_incorrect_program_id, round_up_heap_size, FeatureSet,
|
||||
delay_visibility_of_program_deployment, disable_deploy_of_alloc_free_syscall,
|
||||
enable_bpf_loader_extend_program_ix, enable_bpf_loader_set_authority_checked_ix,
|
||||
enable_program_redeployment_cooldown, limit_max_instruction_trace_length,
|
||||
native_programs_consume_cu, remove_bpf_loader_incorrect_program_id, FeatureSet,
|
||||
},
|
||||
instruction::{AccountMeta, InstructionError},
|
||||
loader_instruction::LoaderInstruction,
|
||||
|
@ -84,6 +81,7 @@ pub fn load_program_from_bytes(
|
|||
deployment_slot: Slot,
|
||||
use_jit: bool,
|
||||
reject_deployment_of_broken_elfs: bool,
|
||||
debugging_features: bool,
|
||||
) -> Result<LoadedProgram, InstructionError> {
|
||||
let mut register_syscalls_time = Measure::start("register_syscalls_time");
|
||||
let disable_deploy_of_alloc_free_syscall = reject_deployment_of_broken_elfs
|
||||
|
@ -93,7 +91,7 @@ pub fn load_program_from_bytes(
|
|||
compute_budget,
|
||||
reject_deployment_of_broken_elfs,
|
||||
disable_deploy_of_alloc_free_syscall,
|
||||
false,
|
||||
debugging_features,
|
||||
)
|
||||
.map_err(|e| {
|
||||
ic_logger_msg!(log_collector, "Failed to register syscalls: {}", e);
|
||||
|
@ -163,6 +161,7 @@ pub fn load_program_from_account(
|
|||
program: &BorrowedAccount,
|
||||
programdata: &BorrowedAccount,
|
||||
use_jit: bool,
|
||||
debugging_features: bool,
|
||||
) -> Result<(Arc<LoadedProgram>, Option<LoadProgramMetrics>), InstructionError> {
|
||||
if !check_loader_id(program.get_owner()) {
|
||||
ic_logger_msg!(
|
||||
|
@ -211,6 +210,7 @@ pub fn load_program_from_account(
|
|||
deployment_slot,
|
||||
use_jit,
|
||||
false, /* reject_deployment_of_broken_elfs */
|
||||
debugging_features,
|
||||
)?);
|
||||
if let Some(mut tx_executor_cache) = tx_executor_cache {
|
||||
tx_executor_cache.set(
|
||||
|
@ -242,7 +242,8 @@ macro_rules! deploy_program {
|
|||
$account_size,
|
||||
$slot,
|
||||
$use_jit,
|
||||
true,
|
||||
true, /* reject_deployment_of_broken_elfs */
|
||||
false, /* debugging_features */
|
||||
)?;
|
||||
if let Some(old_entry) = $invoke_context.tx_executor_cache.borrow().get(&$program_id) {
|
||||
let usage_counter = old_entry.usage_counter.load(Ordering::Relaxed);
|
||||
|
@ -292,7 +293,8 @@ fn check_loader_id(id: &Pubkey) -> bool {
|
|||
|| bpf_loader_upgradeable::check_id(id)
|
||||
}
|
||||
|
||||
fn calculate_heap_cost(heap_size: u64, heap_cost: u64, enable_rounding_fix: bool) -> u64 {
|
||||
/// Only used in macro, do not use directly!
|
||||
pub fn calculate_heap_cost(heap_size: u64, heap_cost: u64, enable_rounding_fix: bool) -> u64 {
|
||||
const KIBIBYTE: u64 = 1024;
|
||||
const PAGE_SIZE_KB: u64 = 32;
|
||||
let mut rounded_heap_size = heap_size;
|
||||
|
@ -306,83 +308,96 @@ fn calculate_heap_cost(heap_size: u64, heap_cost: u64, enable_rounding_fix: bool
|
|||
.saturating_mul(heap_cost)
|
||||
}
|
||||
|
||||
/// Create the SBF virtual machine
|
||||
pub fn create_ebpf_vm<'a, 'b>(
|
||||
/// Only used in macro, do not use directly!
|
||||
pub fn create_vm<'a, 'b>(
|
||||
program: &'a VerifiedExecutable<RequisiteVerifier, InvokeContext<'b>>,
|
||||
stack: &'a mut AlignedMemory<HOST_ALIGN>,
|
||||
heap: AlignedMemory<HOST_ALIGN>,
|
||||
regions: Vec<MemoryRegion>,
|
||||
orig_account_lengths: Vec<usize>,
|
||||
invoke_context: &'a mut InvokeContext<'b>,
|
||||
stack: &mut AlignedMemory<HOST_ALIGN>,
|
||||
heap: &mut AlignedMemory<HOST_ALIGN>,
|
||||
) -> Result<EbpfVm<'a, RequisiteVerifier, InvokeContext<'b>>, Box<dyn std::error::Error>> {
|
||||
let round_up_heap_size = invoke_context
|
||||
.feature_set
|
||||
.is_active(&round_up_heap_size::id());
|
||||
let heap_cost_result = invoke_context.consume_checked(calculate_heap_cost(
|
||||
heap.len() as u64,
|
||||
invoke_context.get_compute_budget().heap_cost,
|
||||
round_up_heap_size,
|
||||
));
|
||||
if round_up_heap_size {
|
||||
heap_cost_result?;
|
||||
}
|
||||
let check_aligned = bpf_loader_deprecated::id()
|
||||
!= invoke_context
|
||||
.transaction_context
|
||||
.get_current_instruction_context()
|
||||
.and_then(|instruction_context| {
|
||||
instruction_context
|
||||
.try_borrow_last_program_account(invoke_context.transaction_context)
|
||||
})
|
||||
.map(|program_account| *program_account.get_owner())?;
|
||||
let check_size = invoke_context
|
||||
.feature_set
|
||||
.is_active(&check_slice_translation_size::id());
|
||||
let allocator = Rc::new(RefCell::new(BpfAllocator::new(heap, MM_HEAP_START)));
|
||||
invoke_context.set_syscall_context(
|
||||
check_aligned,
|
||||
check_size,
|
||||
let stack_size = stack.len();
|
||||
let heap_size = heap.len();
|
||||
let memory_mapping =
|
||||
create_memory_mapping(program.get_executable(), stack, heap, regions, None)?;
|
||||
invoke_context.set_syscall_context(SyscallContext {
|
||||
allocator: BpfAllocator::new(heap_size as u64),
|
||||
orig_account_lengths,
|
||||
allocator.clone(),
|
||||
)?;
|
||||
let stack_len = stack.len();
|
||||
let memory_mapping = create_memory_mapping(
|
||||
program.get_executable(),
|
||||
stack,
|
||||
allocator.borrow_mut().heap_mut(),
|
||||
regions,
|
||||
None,
|
||||
)?;
|
||||
|
||||
trace_log: Vec::new(),
|
||||
})?;
|
||||
Ok(EbpfVm::new(
|
||||
program,
|
||||
invoke_context,
|
||||
memory_mapping,
|
||||
stack_len,
|
||||
stack_size,
|
||||
))
|
||||
}
|
||||
|
||||
/// Create the SBF virtual machine
|
||||
#[macro_export]
|
||||
macro_rules! create_vm {
|
||||
($vm_name:ident, $executable:expr, $stack:ident, $heap:ident, $additional_regions:expr, $orig_account_lengths:expr, $invoke_context:expr) => {
|
||||
let mut $stack = solana_rbpf::aligned_memory::AlignedMemory::<
|
||||
{ solana_rbpf::ebpf::HOST_ALIGN },
|
||||
>::zero_filled($executable.get_executable().get_config().stack_size());
|
||||
|
||||
// this is needed if the caller passes "&mut invoke_context" to the
|
||||
// macro. The lint complains that (&mut invoke_context).get_compute_budget()
|
||||
// does an unnecessary mutable borrow
|
||||
#[allow(clippy::unnecessary_mut_passed)]
|
||||
let heap_size = $invoke_context
|
||||
($vm:ident, $program:expr, $regions:expr, $orig_account_lengths:expr, $invoke_context:expr $(,)?) => {
|
||||
let invoke_context = &*$invoke_context;
|
||||
let stack_size = $program.get_executable().get_config().stack_size();
|
||||
let heap_size = invoke_context
|
||||
.get_compute_budget()
|
||||
.heap_size
|
||||
.unwrap_or(solana_sdk::entrypoint::HEAP_LENGTH);
|
||||
let $heap = solana_rbpf::aligned_memory::AlignedMemory::<{ solana_rbpf::ebpf::HOST_ALIGN }>::zero_filled(heap_size);
|
||||
let round_up_heap_size = invoke_context
|
||||
.feature_set
|
||||
.is_active(&solana_sdk::feature_set::round_up_heap_size::id());
|
||||
let mut heap_cost_result = invoke_context.consume_checked($crate::calculate_heap_cost(
|
||||
heap_size as u64,
|
||||
invoke_context.get_compute_budget().heap_cost,
|
||||
round_up_heap_size,
|
||||
));
|
||||
if !round_up_heap_size {
|
||||
heap_cost_result = Ok(());
|
||||
}
|
||||
let mut allocations = None;
|
||||
let $vm = heap_cost_result.and_then(|_| {
|
||||
let mut stack = solana_rbpf::aligned_memory::AlignedMemory::<
|
||||
{ solana_rbpf::ebpf::HOST_ALIGN },
|
||||
>::zero_filled(stack_size);
|
||||
let mut heap = solana_rbpf::aligned_memory::AlignedMemory::<
|
||||
{ solana_rbpf::ebpf::HOST_ALIGN },
|
||||
>::zero_filled(heap_size);
|
||||
let vm = $crate::create_vm(
|
||||
$program,
|
||||
$regions,
|
||||
$orig_account_lengths,
|
||||
$invoke_context,
|
||||
&mut stack,
|
||||
&mut heap,
|
||||
);
|
||||
allocations = Some((stack, heap));
|
||||
vm
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
let $vm_name = create_ebpf_vm(
|
||||
$executable,
|
||||
&mut $stack,
|
||||
$heap,
|
||||
#[macro_export]
|
||||
macro_rules! mock_create_vm {
|
||||
($vm:ident, $additional_regions:expr, $orig_account_lengths:expr, $invoke_context:expr $(,)?) => {
|
||||
let loader = std::sync::Arc::new(BuiltInProgram::new_loader(
|
||||
solana_rbpf::vm::Config::default(),
|
||||
));
|
||||
let function_registry = solana_rbpf::vm::FunctionRegistry::default();
|
||||
let executable = solana_rbpf::elf::Executable::<InvokeContext>::from_text_bytes(
|
||||
&[0x95, 0, 0, 0, 0, 0, 0, 0],
|
||||
loader,
|
||||
function_registry,
|
||||
)
|
||||
.unwrap();
|
||||
let verified_executable = solana_rbpf::vm::VerifiedExecutable::<
|
||||
solana_rbpf::verifier::RequisiteVerifier,
|
||||
InvokeContext,
|
||||
>::from_executable(executable)
|
||||
.unwrap();
|
||||
$crate::create_vm!(
|
||||
$vm,
|
||||
&verified_executable,
|
||||
$additional_regions,
|
||||
$orig_account_lengths,
|
||||
$invoke_context,
|
||||
|
@ -409,7 +424,7 @@ fn create_memory_mapping<'a, 'b, C: ContextObject>(
|
|||
0
|
||||
},
|
||||
),
|
||||
MemoryRegion::new_writable(heap.as_slice_mut(), ebpf::MM_HEAP_START),
|
||||
MemoryRegion::new_writable(heap.as_slice_mut(), MM_HEAP_START),
|
||||
]
|
||||
.into_iter()
|
||||
.chain(additional_regions.into_iter())
|
||||
|
@ -580,6 +595,7 @@ fn process_instruction_inner(
|
|||
&program_account,
|
||||
programdata_account.as_ref().unwrap_or(&program_account),
|
||||
use_jit,
|
||||
false, /* debugging_features */
|
||||
)?;
|
||||
drop(program_account);
|
||||
drop(programdata_account);
|
||||
|
@ -1553,11 +1569,9 @@ fn execute<'a, 'b: 'a>(
|
|||
executable,
|
||||
)
|
||||
},
|
||||
stack,
|
||||
heap,
|
||||
regions,
|
||||
account_lengths,
|
||||
invoke_context
|
||||
invoke_context,
|
||||
);
|
||||
let mut vm = match vm {
|
||||
Ok(info) => info,
|
||||
|
@ -1620,7 +1634,7 @@ fn execute<'a, 'b: 'a>(
|
|||
.transaction_context
|
||||
.get_current_instruction_context()?,
|
||||
parameter_bytes,
|
||||
invoke_context.get_orig_account_lengths()?,
|
||||
&invoke_context.get_syscall_context()?.orig_account_lengths,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -1652,10 +1666,8 @@ mod tests {
|
|||
invoke_context::mock_process_instruction, with_mock_invoke_context,
|
||||
},
|
||||
solana_rbpf::{
|
||||
ebpf::MM_INPUT_START,
|
||||
elf::Executable,
|
||||
verifier::{Verifier, VerifierError},
|
||||
vm::{BuiltInProgram, Config, ContextObject, FunctionRegistry},
|
||||
vm::{Config, ContextObject, FunctionRegistry},
|
||||
},
|
||||
solana_sdk::{
|
||||
account::{
|
||||
|
@ -1729,54 +1741,6 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "ExceededMaxInstructions(31)")]
|
||||
fn test_bpf_loader_non_terminating_program() {
|
||||
#[rustfmt::skip]
|
||||
let program = &[
|
||||
0x07, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // r6 + 1
|
||||
0x05, 0x00, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, // goto -2
|
||||
0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // exit
|
||||
];
|
||||
let mut input_mem = [0x00];
|
||||
let bpf_functions = std::collections::BTreeMap::<u32, (usize, String)>::new();
|
||||
let executable = Executable::<TestContextObject>::from_text_bytes(
|
||||
program,
|
||||
Arc::new(BuiltInProgram::new_loader(Config::default())),
|
||||
bpf_functions,
|
||||
)
|
||||
.unwrap();
|
||||
let verified_executable =
|
||||
VerifiedExecutable::<TautologyVerifier, TestContextObject>::from_executable(executable)
|
||||
.unwrap();
|
||||
let input_region = MemoryRegion::new_writable(&mut input_mem, MM_INPUT_START);
|
||||
let mut context_object = TestContextObject { remaining: 10 };
|
||||
let mut stack = AlignedMemory::zero_filled(
|
||||
verified_executable
|
||||
.get_executable()
|
||||
.get_config()
|
||||
.stack_size(),
|
||||
);
|
||||
let mut heap = AlignedMemory::with_capacity(0);
|
||||
let stack_len = stack.len();
|
||||
|
||||
let memory_mapping = create_memory_mapping(
|
||||
verified_executable.get_executable(),
|
||||
&mut stack,
|
||||
&mut heap,
|
||||
vec![input_region],
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
let mut vm = EbpfVm::new(
|
||||
&verified_executable,
|
||||
&mut context_object,
|
||||
memory_mapping,
|
||||
stack_len,
|
||||
);
|
||||
vm.execute_program(true).1.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "LDDWCannotBeLast")]
|
||||
fn test_bpf_loader_check_load_dw() {
|
||||
|
|
|
@ -675,7 +675,10 @@ where
|
|||
|
||||
// unwrapping here is fine: we're in a syscall and the method below fails
|
||||
// only outside syscalls
|
||||
let orig_data_lens = invoke_context.get_orig_account_lengths().unwrap();
|
||||
let orig_data_lens = &invoke_context
|
||||
.get_syscall_context()
|
||||
.unwrap()
|
||||
.orig_account_lengths;
|
||||
|
||||
for (instruction_account_index, instruction_account) in instruction_accounts.iter().enumerate()
|
||||
{
|
||||
|
@ -1083,12 +1086,9 @@ fn update_caller_account(
|
|||
mod tests {
|
||||
use {
|
||||
super::*,
|
||||
crate::allocator_bump::BpfAllocator,
|
||||
crate::mock_create_vm,
|
||||
solana_program_runtime::with_mock_invoke_context,
|
||||
solana_rbpf::{
|
||||
aligned_memory::AlignedMemory, ebpf::MM_INPUT_START, memory_region::MemoryRegion,
|
||||
vm::Config,
|
||||
},
|
||||
solana_rbpf::{ebpf::MM_INPUT_START, memory_region::MemoryRegion, vm::Config},
|
||||
solana_sdk::{
|
||||
account::{Account, AccountSharedData},
|
||||
clock::Epoch,
|
||||
|
@ -1502,17 +1502,12 @@ mod tests {
|
|||
&[0],
|
||||
&[1, 1]
|
||||
);
|
||||
invoke_context
|
||||
.set_syscall_context(
|
||||
true,
|
||||
true,
|
||||
vec![original_data_len],
|
||||
Rc::new(RefCell::new(BpfAllocator::new(
|
||||
AlignedMemory::with_capacity(0),
|
||||
0,
|
||||
))),
|
||||
)
|
||||
.unwrap();
|
||||
mock_create_vm!(
|
||||
_vm,
|
||||
Vec::new(),
|
||||
vec![original_data_len],
|
||||
&mut invoke_context
|
||||
);
|
||||
|
||||
let vm_addr = MM_INPUT_START;
|
||||
let (_mem, region) = MockAccountInfo::new(key, &account).into_region(vm_addr);
|
||||
|
|
|
@ -530,11 +530,6 @@ declare_syscall!(
|
|||
_arg5: u64,
|
||||
_memory_mapping: &mut MemoryMapping,
|
||||
) -> Result<u64, Error> {
|
||||
let allocator = invoke_context.get_allocator()?;
|
||||
let mut allocator = allocator
|
||||
.try_borrow_mut()
|
||||
.map_err(|_| SyscallError::InvokeContextBorrowFailed)?;
|
||||
|
||||
let align = if invoke_context.get_check_aligned() {
|
||||
BPF_ALIGN_OF_U128
|
||||
} else {
|
||||
|
@ -546,13 +541,14 @@ declare_syscall!(
|
|||
return Ok(0);
|
||||
}
|
||||
};
|
||||
let allocator = &mut invoke_context.get_syscall_context_mut()?.allocator;
|
||||
if free_addr == 0 {
|
||||
match allocator.alloc(layout) {
|
||||
Ok(addr) => Ok(addr),
|
||||
Err(_) => Ok(0),
|
||||
}
|
||||
} else {
|
||||
allocator.dealloc(free_addr, layout);
|
||||
// Unimplemented
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
@ -1802,12 +1798,10 @@ mod tests {
|
|||
use solana_sdk::sysvar::fees::Fees;
|
||||
use {
|
||||
super::*,
|
||||
crate::BpfAllocator,
|
||||
crate::mock_create_vm,
|
||||
core::slice,
|
||||
solana_program_runtime::{invoke_context::InvokeContext, with_mock_invoke_context},
|
||||
solana_rbpf::{
|
||||
aligned_memory::AlignedMemory,
|
||||
ebpf::{self, HOST_ALIGN},
|
||||
error::EbpfError,
|
||||
memory_region::MemoryRegion,
|
||||
vm::{BuiltInFunction, Config},
|
||||
|
@ -2289,63 +2283,46 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_syscall_sol_alloc_free() {
|
||||
let config = Config::default();
|
||||
|
||||
// large alloc
|
||||
{
|
||||
prepare_mockup!(invoke_context, program_id, bpf_loader::id());
|
||||
let mut heap = AlignedMemory::<HOST_ALIGN>::zero_filled(100);
|
||||
let mut memory_mapping = MemoryMapping::new(
|
||||
vec![
|
||||
MemoryRegion::new_readonly(&[], ebpf::MM_PROGRAM_START),
|
||||
MemoryRegion::new_writable_gapped(&mut [], ebpf::MM_STACK_START, 4096),
|
||||
MemoryRegion::new_writable(heap.as_slice_mut(), ebpf::MM_HEAP_START),
|
||||
MemoryRegion::new_writable(&mut [], ebpf::MM_INPUT_START),
|
||||
],
|
||||
&config,
|
||||
)
|
||||
.unwrap();
|
||||
invoke_context
|
||||
.set_syscall_context(
|
||||
true,
|
||||
true,
|
||||
vec![],
|
||||
Rc::new(RefCell::new(BpfAllocator::new(heap, ebpf::MM_HEAP_START))),
|
||||
)
|
||||
.unwrap();
|
||||
mock_create_vm!(vm, Vec::new(), Vec::new(), &mut invoke_context);
|
||||
let mut vm = vm.unwrap();
|
||||
let invoke_context = &mut vm.env.context_object_pointer;
|
||||
let memory_mapping = &mut vm.env.memory_mapping;
|
||||
let mut result = ProgramResult::Ok(0);
|
||||
SyscallAllocFree::call(
|
||||
&mut invoke_context,
|
||||
100,
|
||||
invoke_context,
|
||||
solana_sdk::entrypoint::HEAP_LENGTH as u64,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
&mut memory_mapping,
|
||||
memory_mapping,
|
||||
&mut result,
|
||||
);
|
||||
assert_ne!(result.unwrap(), 0);
|
||||
let mut result = ProgramResult::Ok(0);
|
||||
SyscallAllocFree::call(
|
||||
&mut invoke_context,
|
||||
100,
|
||||
invoke_context,
|
||||
solana_sdk::entrypoint::HEAP_LENGTH as u64,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
&mut memory_mapping,
|
||||
memory_mapping,
|
||||
&mut result,
|
||||
);
|
||||
assert_eq!(result.unwrap(), 0);
|
||||
let mut result = ProgramResult::Ok(0);
|
||||
SyscallAllocFree::call(
|
||||
&mut invoke_context,
|
||||
invoke_context,
|
||||
u64::MAX,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
&mut memory_mapping,
|
||||
memory_mapping,
|
||||
&mut result,
|
||||
);
|
||||
assert_eq!(result.unwrap(), 0);
|
||||
|
@ -2354,48 +2331,25 @@ mod tests {
|
|||
// many small unaligned allocs
|
||||
{
|
||||
prepare_mockup!(invoke_context, program_id, bpf_loader::id());
|
||||
let mut heap = AlignedMemory::<HOST_ALIGN>::zero_filled(100);
|
||||
let mut memory_mapping = MemoryMapping::new(
|
||||
vec![
|
||||
MemoryRegion::new_readonly(&[], ebpf::MM_PROGRAM_START),
|
||||
MemoryRegion::new_writable_gapped(&mut [], ebpf::MM_STACK_START, 4096),
|
||||
MemoryRegion::new_writable(heap.as_slice_mut(), ebpf::MM_HEAP_START),
|
||||
MemoryRegion::new_writable(&mut [], ebpf::MM_INPUT_START),
|
||||
],
|
||||
&config,
|
||||
)
|
||||
.unwrap();
|
||||
invoke_context
|
||||
.set_syscall_context(
|
||||
false,
|
||||
true,
|
||||
vec![],
|
||||
Rc::new(RefCell::new(BpfAllocator::new(heap, ebpf::MM_HEAP_START))),
|
||||
)
|
||||
.unwrap();
|
||||
invoke_context.feature_set = Arc::new(FeatureSet::default());
|
||||
mock_create_vm!(vm, Vec::new(), Vec::new(), &mut invoke_context);
|
||||
let mut vm = vm.unwrap();
|
||||
let invoke_context = &mut vm.env.context_object_pointer;
|
||||
let memory_mapping = &mut vm.env.memory_mapping;
|
||||
for _ in 0..100 {
|
||||
let mut result = ProgramResult::Ok(0);
|
||||
SyscallAllocFree::call(
|
||||
&mut invoke_context,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
&mut memory_mapping,
|
||||
&mut result,
|
||||
);
|
||||
SyscallAllocFree::call(invoke_context, 1, 0, 0, 0, 0, memory_mapping, &mut result);
|
||||
assert_ne!(result.unwrap(), 0);
|
||||
}
|
||||
let mut result = ProgramResult::Ok(0);
|
||||
SyscallAllocFree::call(
|
||||
&mut invoke_context,
|
||||
100,
|
||||
invoke_context,
|
||||
solana_sdk::entrypoint::HEAP_LENGTH as u64,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
&mut memory_mapping,
|
||||
memory_mapping,
|
||||
&mut result,
|
||||
);
|
||||
assert_eq!(result.unwrap(), 0);
|
||||
|
@ -2404,48 +2358,24 @@ mod tests {
|
|||
// many small aligned allocs
|
||||
{
|
||||
prepare_mockup!(invoke_context, program_id, bpf_loader::id());
|
||||
let mut heap = AlignedMemory::<HOST_ALIGN>::zero_filled(100);
|
||||
let mut memory_mapping = MemoryMapping::new(
|
||||
vec![
|
||||
MemoryRegion::new_readonly(&[], ebpf::MM_PROGRAM_START),
|
||||
MemoryRegion::new_writable_gapped(&mut [], ebpf::MM_STACK_START, 4096),
|
||||
MemoryRegion::new_writable(heap.as_slice_mut(), ebpf::MM_HEAP_START),
|
||||
MemoryRegion::new_writable(&mut [], ebpf::MM_INPUT_START),
|
||||
],
|
||||
&config,
|
||||
)
|
||||
.unwrap();
|
||||
invoke_context
|
||||
.set_syscall_context(
|
||||
true,
|
||||
true,
|
||||
vec![],
|
||||
Rc::new(RefCell::new(BpfAllocator::new(heap, ebpf::MM_HEAP_START))),
|
||||
)
|
||||
.unwrap();
|
||||
mock_create_vm!(vm, Vec::new(), Vec::new(), &mut invoke_context);
|
||||
let mut vm = vm.unwrap();
|
||||
let invoke_context = &mut vm.env.context_object_pointer;
|
||||
let memory_mapping = &mut vm.env.memory_mapping;
|
||||
for _ in 0..12 {
|
||||
let mut result = ProgramResult::Ok(0);
|
||||
SyscallAllocFree::call(
|
||||
&mut invoke_context,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
&mut memory_mapping,
|
||||
&mut result,
|
||||
);
|
||||
SyscallAllocFree::call(invoke_context, 1, 0, 0, 0, 0, memory_mapping, &mut result);
|
||||
assert_ne!(result.unwrap(), 0);
|
||||
}
|
||||
let mut result = ProgramResult::Ok(0);
|
||||
SyscallAllocFree::call(
|
||||
&mut invoke_context,
|
||||
100,
|
||||
invoke_context,
|
||||
solana_sdk::entrypoint::HEAP_LENGTH as u64,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
&mut memory_mapping,
|
||||
memory_mapping,
|
||||
&mut result,
|
||||
);
|
||||
assert_eq!(result.unwrap(), 0);
|
||||
|
@ -2455,35 +2385,19 @@ mod tests {
|
|||
|
||||
fn aligned<T>() {
|
||||
prepare_mockup!(invoke_context, program_id, bpf_loader::id());
|
||||
let mut heap = AlignedMemory::<HOST_ALIGN>::zero_filled(100);
|
||||
let config = Config::default();
|
||||
let mut memory_mapping = MemoryMapping::new(
|
||||
vec![
|
||||
MemoryRegion::new_readonly(&[], ebpf::MM_PROGRAM_START),
|
||||
MemoryRegion::new_writable_gapped(&mut [], ebpf::MM_STACK_START, 4096),
|
||||
MemoryRegion::new_writable(heap.as_slice_mut(), ebpf::MM_HEAP_START),
|
||||
MemoryRegion::new_writable(&mut [], ebpf::MM_INPUT_START),
|
||||
],
|
||||
&config,
|
||||
)
|
||||
.unwrap();
|
||||
invoke_context
|
||||
.set_syscall_context(
|
||||
true,
|
||||
true,
|
||||
vec![],
|
||||
Rc::new(RefCell::new(BpfAllocator::new(heap, ebpf::MM_HEAP_START))),
|
||||
)
|
||||
.unwrap();
|
||||
mock_create_vm!(vm, Vec::new(), Vec::new(), &mut invoke_context);
|
||||
let mut vm = vm.unwrap();
|
||||
let invoke_context = &mut vm.env.context_object_pointer;
|
||||
let memory_mapping = &mut vm.env.memory_mapping;
|
||||
let mut result = ProgramResult::Ok(0);
|
||||
SyscallAllocFree::call(
|
||||
&mut invoke_context,
|
||||
invoke_context,
|
||||
size_of::<T>() as u64,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
&mut memory_mapping,
|
||||
memory_mapping,
|
||||
&mut result,
|
||||
);
|
||||
let address = result.unwrap();
|
||||
|
|
|
@ -455,7 +455,7 @@ pub fn process_instruction_deploy(
|
|||
invoke_context.get_log_collector(),
|
||||
buffer,
|
||||
use_jit,
|
||||
false,
|
||||
false, /* debugging_features */
|
||||
)?;
|
||||
load_program_metrics.submit_datapoint(&mut invoke_context.timings);
|
||||
if let Some(mut source_program) = source_program {
|
||||
|
@ -588,7 +588,7 @@ pub fn process_instruction(
|
|||
invoke_context.get_log_collector(),
|
||||
&program,
|
||||
use_jit,
|
||||
false,
|
||||
false, /* debugging_features */
|
||||
)?;
|
||||
load_program_metrics.submit_datapoint(&mut invoke_context.timings);
|
||||
get_or_create_executor_time.stop();
|
||||
|
|
|
@ -12,7 +12,7 @@ extern crate solana_bpf_loader_program;
|
|||
use {
|
||||
byteorder::{ByteOrder, LittleEndian, WriteBytesExt},
|
||||
solana_bpf_loader_program::{
|
||||
create_ebpf_vm, create_vm, serialization::serialize_parameters, syscalls::create_loader,
|
||||
create_vm, serialization::serialize_parameters, syscalls::create_loader,
|
||||
},
|
||||
solana_measure::measure::Measure,
|
||||
solana_program_runtime::{compute_budget::ComputeBudget, invoke_context::InvokeContext},
|
||||
|
@ -131,11 +131,9 @@ fn bench_program_alu(bencher: &mut Bencher) {
|
|||
create_vm!(
|
||||
vm,
|
||||
&verified_executable,
|
||||
stack,
|
||||
heap,
|
||||
vec![MemoryRegion::new_writable(&mut inner_iter, MM_INPUT_START)],
|
||||
vec![],
|
||||
&mut invoke_context
|
||||
&mut invoke_context,
|
||||
);
|
||||
let mut vm = vm.unwrap();
|
||||
|
||||
|
@ -258,13 +256,11 @@ fn bench_create_vm(bencher: &mut Bencher) {
|
|||
create_vm!(
|
||||
vm,
|
||||
&verified_executable,
|
||||
stack,
|
||||
heap,
|
||||
clone_regions(®ions),
|
||||
account_lengths.clone(),
|
||||
&mut invoke_context
|
||||
&mut invoke_context,
|
||||
);
|
||||
let _ = vm.unwrap();
|
||||
vm.unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -303,11 +299,9 @@ fn bench_instruction_count_tuner(_bencher: &mut Bencher) {
|
|||
create_vm!(
|
||||
vm,
|
||||
&verified_executable,
|
||||
stack,
|
||||
heap,
|
||||
regions,
|
||||
account_lengths,
|
||||
&mut invoke_context
|
||||
&mut invoke_context,
|
||||
);
|
||||
let mut vm = vm.unwrap();
|
||||
|
||||
|
|
|
@ -3,9 +3,14 @@ use {
|
|||
serde::{Deserialize, Serialize},
|
||||
serde_json::Result,
|
||||
solana_bpf_loader_program::{
|
||||
create_ebpf_vm, create_vm, serialization::serialize_parameters, syscalls::create_loader,
|
||||
create_vm, load_program_from_bytes, serialization::serialize_parameters,
|
||||
syscalls::create_loader,
|
||||
},
|
||||
solana_program_runtime::{
|
||||
invoke_context::InvokeContext,
|
||||
loaded_programs::{LoadProgramMetrics, LoadedProgramType},
|
||||
with_mock_invoke_context,
|
||||
},
|
||||
solana_program_runtime::{invoke_context::InvokeContext, with_mock_invoke_context},
|
||||
solana_rbpf::{
|
||||
assembler::assemble, elf::Executable, static_analysis::Analysis,
|
||||
verifier::RequisiteVerifier, vm::VerifiedExecutable,
|
||||
|
@ -14,6 +19,7 @@ use {
|
|||
account::AccountSharedData,
|
||||
bpf_loader,
|
||||
pubkey::Pubkey,
|
||||
slot_history::Slot,
|
||||
transaction_context::{IndexOfAccount, InstructionAccount},
|
||||
},
|
||||
std::{
|
||||
|
@ -216,7 +222,6 @@ before execting it in the virtual machine.",
|
|||
}
|
||||
};
|
||||
with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
|
||||
invoke_context.enable_instruction_tracing = true;
|
||||
invoke_context
|
||||
.transaction_context
|
||||
.get_next_instruction_context()
|
||||
|
@ -240,28 +245,45 @@ before execting it in the virtual machine.",
|
|||
file.rewind().unwrap();
|
||||
let mut contents = Vec::new();
|
||||
file.read_to_end(&mut contents).unwrap();
|
||||
let loader = create_loader(
|
||||
&invoke_context.feature_set,
|
||||
&ComputeBudget::default(),
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
)
|
||||
.unwrap();
|
||||
let executable = if magic == [0x7f, 0x45, 0x4c, 0x46] {
|
||||
Executable::<InvokeContext>::from_elf(&contents, loader)
|
||||
.map_err(|err| format!("Executable constructor failed: {err:?}"))
|
||||
let mut verified_executable = if magic == [0x7f, 0x45, 0x4c, 0x46] {
|
||||
let mut load_program_metrics = LoadProgramMetrics::default();
|
||||
let result = load_program_from_bytes(
|
||||
&invoke_context.feature_set,
|
||||
invoke_context.get_compute_budget(),
|
||||
None,
|
||||
&mut load_program_metrics,
|
||||
&contents,
|
||||
&bpf_loader::id(),
|
||||
contents.len(),
|
||||
Slot::default(),
|
||||
false, /* use_jit */
|
||||
true, /* reject_deployment_of_broken_elfs */
|
||||
true, /* debugging_features */
|
||||
);
|
||||
match result {
|
||||
Ok(loaded_program) => match loaded_program.program {
|
||||
LoadedProgramType::LegacyV1(program) => Ok(unsafe { std::mem::transmute(program) }),
|
||||
_ => unreachable!(),
|
||||
},
|
||||
Err(err) => Err(format!("Loading executable failed: {err:?}")),
|
||||
}
|
||||
} else {
|
||||
assemble::<InvokeContext>(std::str::from_utf8(contents.as_slice()).unwrap(), loader)
|
||||
let loader = create_loader(
|
||||
&invoke_context.feature_set,
|
||||
invoke_context.get_compute_budget(),
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
)
|
||||
.unwrap();
|
||||
let executable =
|
||||
assemble::<InvokeContext>(std::str::from_utf8(contents.as_slice()).unwrap(), loader)
|
||||
.unwrap();
|
||||
VerifiedExecutable::<RequisiteVerifier, InvokeContext>::from_executable(executable)
|
||||
.map_err(|err| format!("Assembling executable failed: {err:?}"))
|
||||
}
|
||||
.unwrap();
|
||||
|
||||
#[allow(unused_mut)]
|
||||
let mut verified_executable =
|
||||
VerifiedExecutable::<RequisiteVerifier, InvokeContext>::from_executable(executable)
|
||||
.map_err(|err| format!("Executable verifier failed: {err:?}"))
|
||||
.unwrap();
|
||||
|
||||
#[cfg(all(not(target_os = "windows"), target_arch = "x86_64"))]
|
||||
verified_executable.jit_compile().unwrap();
|
||||
let mut analysis = LazyAnalysis::new(verified_executable.get_executable());
|
||||
|
@ -285,11 +307,9 @@ before execting it in the virtual machine.",
|
|||
create_vm!(
|
||||
vm,
|
||||
&verified_executable,
|
||||
stack,
|
||||
heap,
|
||||
regions,
|
||||
account_lengths,
|
||||
&mut invoke_context
|
||||
&mut invoke_context,
|
||||
);
|
||||
let mut vm = vm.unwrap();
|
||||
let start_time = Instant::now();
|
||||
|
@ -299,14 +319,17 @@ before execting it in the virtual machine.",
|
|||
let (instruction_count, result) = vm.execute_program(matches.value_of("use").unwrap() != "jit");
|
||||
let duration = Instant::now() - start_time;
|
||||
if matches.occurrences_of("trace") > 0 {
|
||||
for (frame, trace) in vm
|
||||
for (frame, syscall_context) in vm
|
||||
.env
|
||||
.context_object_pointer
|
||||
.trace_log_stack
|
||||
.syscall_context
|
||||
.iter()
|
||||
.enumerate()
|
||||
{
|
||||
let trace_log = trace.trace_log.as_slice();
|
||||
if syscall_context.is_none() {
|
||||
continue;
|
||||
}
|
||||
let trace_log = syscall_context.as_ref().unwrap().trace_log.as_slice();
|
||||
if matches.value_of("trace").unwrap() == "stdout" {
|
||||
writeln!(&mut std::io::stdout(), "Frame {frame}").unwrap();
|
||||
analysis
|
||||
|
|
|
@ -4271,6 +4271,7 @@ impl Bank {
|
|||
&program,
|
||||
programdata.as_ref().unwrap_or(&program),
|
||||
self.runtime_config.bpf_jit,
|
||||
false, /* debugging_features */
|
||||
)
|
||||
.map(|(loaded_program, _create_executor_metrics)| loaded_program)
|
||||
.map_err(|err| TransactionError::InstructionError(0, err))
|
||||
|
|
Loading…
Reference in New Issue