From fada00325ce84ddc16f3d686e90a1c15b3735695 Mon Sep 17 00:00:00 2001 From: febo Date: Tue, 22 Apr 2025 10:43:14 +0100 Subject: [PATCH 1/4] Improve allocators set up --- sdk/pinocchio/src/entrypoint/mod.rs | 104 +++++++++++++++++++++------- 1 file changed, 78 insertions(+), 26 deletions(-) diff --git a/sdk/pinocchio/src/entrypoint/mod.rs b/sdk/pinocchio/src/entrypoint/mod.rs index 498dfa7a7..4353651f0 100644 --- a/sdk/pinocchio/src/entrypoint/mod.rs +++ b/sdk/pinocchio/src/entrypoint/mod.rs @@ -89,6 +89,18 @@ pub const SUCCESS: u64 = super::SUCCESS; /// /// } /// ``` +/// +/// # Important +/// +/// The panic handler set up is different dependending on whether the `std` library is available to the +/// linker or not. The `entrypoint!` macro will set up a default panic "hook", that works with the +/// `#[panic_handler]` set by the `std`. Therefore, this macro should be used when the program or any +/// of its dependencies are dependent on the `std` library. +/// +/// When the program and all its dependencies are `no_std`, it is necessary to set a +/// `#[panic_handler]` to handle panics. This is done by the `nostd_panic_handler!` macro. In this case, +/// it is not possible to use the `entrypoint` macro. Use the `program_entrypoint!` macro instead and set +/// up the allocator and panic handler manually. #[macro_export] macro_rules! entrypoint { ( $process_instruction:ident ) => { @@ -223,8 +235,8 @@ pub unsafe fn deserialize<'a, const MAX_ACCOUNTS: usize>( /// Default panic hook. /// /// This macro sets up a default panic hook that logs the panic message and the file where the -/// panic occurred. Syscall "abort()" will be called after it returns. It acts as a hook after -/// rust runtime panics. +/// panic occurred. It acts as a hook after Rust runtime panics; syscall `abort()` will be called +/// after it returns. /// /// Note that this requires the `"std"` feature to be enabled. #[cfg(feature = "std")] @@ -243,9 +255,11 @@ macro_rules! default_panic_handler { /// Default panic hook. /// -/// This macro sets up a default panic hook that logs the file where the panic occurred. +/// This macro sets up a default panic hook that logs the file where the panic occurred. It acts +/// as a hook after Rust runtime panics; syscall `abort()` will be called after it returns. /// -/// This is used when the `"std"` feature is disabled. +/// This is used when the `"std"` feature is disabled, while either the program or any of its +/// dependencies are not `no_std`. #[cfg(not(feature = "std"))] #[macro_export] macro_rules! default_panic_handler { @@ -265,6 +279,9 @@ macro_rules! default_panic_handler { /// A global `#[panic_handler]` for `no_std` programs. /// +/// This macro sets up a default panic handler that logs the location (file, line and column) +/// where the panic occurred and then calls the syscall `abort()`. +/// /// This macro can only be used when all crates are `no_std` and the `"std"` feature is /// disabled. #[cfg(not(feature = "std"))] @@ -292,14 +309,13 @@ macro_rules! nostd_panic_handler { } } - // A placeholder for `cargo clippy`. - // - // Add `panic = "abort"` to `[profile.dev]` in `Cargo.toml` for clippy. + /// A panic handler for when the program is compiled on a target different than + /// `"solana"`. + /// + /// This links the `std` library, which will set up a default panic handler. #[cfg(not(target_os = "solana"))] - #[no_mangle] - #[panic_handler] - fn handler(_info: &core::panic::PanicInfo<'_>) -> ! { - unsafe { core::hint::unreachable_unchecked() } + mod __private { + extern crate std as __std; } }; } @@ -317,10 +333,14 @@ macro_rules! default_allocator { len: $crate::entrypoint::HEAP_LENGTH, }; - // A placeholder for `cargo clippy`. - #[cfg(not(any(target_os = "solana", test)))] - #[global_allocator] - static A: $crate::entrypoint::NoAllocator = $crate::entrypoint::NoAllocator; + /// A default allocator for when the program is compiled on a target different than + /// `"solana"`. + /// + /// This links the `std` library, which will set up a default global allocator. + #[cfg(not(target_os = "solana"))] + mod __private { + extern crate std as __std; + } }; } @@ -337,12 +357,30 @@ macro_rules! no_allocator { /// A global allocator that does not allocate memory. /// -/// This macro sets up a global allocator that denies all allocations. This is useful when the -/// program does not need to allocate memory $mdash; the program will panic if it tries to -/// allocate memory. +/// This macro sets up a global allocator that denies all allocations. This is useful +/// when the program does not need to allocate memory. The program will panic if it tries +/// to allocate memory. +#[deprecated(since = "0.8.4", note = "Use the `static_allocator` macro instead")] #[cfg(not(feature = "std"))] #[macro_export] macro_rules! no_allocator { + () => { + $crate::static_allocator!(); + }; +} + +/// A global allocator that does not dynamically allocate memory. +/// +/// This macro sets up a global allocator that denies all dynamic allocations, while +/// allowing static ("manual") allocations. This is useful when the program does not need to +/// synamically allocate memory and manages their own allocations. +/// +/// The program will panic if it tries to allocate memory. +/// +/// This is used when the `"std"` feature is disabled. +#[cfg(not(feature = "std"))] +#[macro_export] +macro_rules! static_allocator { () => { #[cfg(target_os = "solana")] #[global_allocator] @@ -351,9 +389,6 @@ macro_rules! no_allocator { /// Allocates memory for the given type `T` at the specified offset in the /// heap reserved address space. /// - /// This function requires the `#![feature(const_mut_refs)]` crate feature - /// to be set. - /// /// # Safety /// /// It is the caller's responsibility to ensure that the offset does not @@ -363,8 +398,17 @@ macro_rules! no_allocator { /// For types that cannot hold the bit-pattern `0` as a valid value, use /// `core::mem::MaybeUninit` to allocate memory for the type and /// initialize it later. - #[inline] - pub const unsafe fn allocate_unchecked(offset: usize) -> &'static mut T { + // + // Make this `const` once `const_mut_refs` is stable for the platform-tools + // toolchain Rust version. + #[inline(always)] + pub unsafe fn allocate_unchecked(offset: usize) -> &'static mut T { + // SAFETY: The pointer is within a valid range and aligned to `T`. + unsafe { &mut *(calculate_offset::(offset) as *mut T) } + } + + #[inline(always)] + const fn calculate_offset(offset: usize) -> usize { let start = $crate::entrypoint::HEAP_START_ADDRESS as usize + offset; let end = start + core::mem::size_of::(); @@ -381,8 +425,16 @@ macro_rules! no_allocator { "offset is not aligned" ); - // SAFETY: The pointer is within a valid range and aligned to `T`. - unsafe { &mut *(start as *mut T) } + start + } + + /// A default allocator for when the program is compiled on a target different than + /// `"solana"`. + /// + /// This links the `std` library, which will set up a default global allocator. + #[cfg(not(target_os = "solana"))] + mod __private { + extern crate std as __std; } }; } @@ -430,7 +482,7 @@ mod alloc { } #[cfg(not(feature = "std"))] -/// Zero global allocator. +/// An allocator that does not allocate memory. pub struct NoAllocator; #[cfg(not(feature = "std"))] From bd55daa4ad2e9dc6c42d4e033af62e21365bbc7b Mon Sep 17 00:00:00 2001 From: febo Date: Tue, 22 Apr 2025 23:35:13 +0100 Subject: [PATCH 2/4] Avoid name clashes --- sdk/pinocchio/src/entrypoint/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sdk/pinocchio/src/entrypoint/mod.rs b/sdk/pinocchio/src/entrypoint/mod.rs index 4353651f0..bfe89cfec 100644 --- a/sdk/pinocchio/src/entrypoint/mod.rs +++ b/sdk/pinocchio/src/entrypoint/mod.rs @@ -314,7 +314,7 @@ macro_rules! nostd_panic_handler { /// /// This links the `std` library, which will set up a default panic handler. #[cfg(not(target_os = "solana"))] - mod __private { + mod __private_panic_handler { extern crate std as __std; } }; @@ -338,7 +338,7 @@ macro_rules! default_allocator { /// /// This links the `std` library, which will set up a default global allocator. #[cfg(not(target_os = "solana"))] - mod __private { + mod __private_alloc { extern crate std as __std; } }; @@ -433,7 +433,7 @@ macro_rules! static_allocator { /// /// This links the `std` library, which will set up a default global allocator. #[cfg(not(target_os = "solana"))] - mod __private { + mod __private_alloc { extern crate std as __std; } }; From 053670474f92ba883e7e2cdeebe717a29de25ad7 Mon Sep 17 00:00:00 2001 From: febo Date: Wed, 23 Apr 2025 02:05:01 +0100 Subject: [PATCH 3/4] Deprecate entrypoint macro --- sdk/pinocchio/src/entrypoint/mod.rs | 65 +++++++++++++++++++---------- 1 file changed, 44 insertions(+), 21 deletions(-) diff --git a/sdk/pinocchio/src/entrypoint/mod.rs b/sdk/pinocchio/src/entrypoint/mod.rs index bfe89cfec..dc4efd81a 100644 --- a/sdk/pinocchio/src/entrypoint/mod.rs +++ b/sdk/pinocchio/src/entrypoint/mod.rs @@ -32,15 +32,34 @@ pub const SUCCESS: u64 = super::SUCCESS; /// Declare the program entrypoint and set up global handlers. /// -/// The main difference from the standard `entrypoint!` macro is that this macro represents an -/// entrypoint that does not perform allocations or copies when reading the input buffer. +/// This macro is deprecated and will be removed in a future version. It is recommended to +/// use the [`entrypoint_with_allocator_and_panic_handler`] macro instead. +#[deprecated( + since = "0.8.4", + note = "Use the `entrypoint_with_allocator_and_panic_handler` macro instead" +)] +#[macro_export] +macro_rules! entrypoint { + ( $process_instruction:ident ) => { + entrypoint!($process_instruction, { $crate::MAX_TX_ACCOUNTS }); + }; + ( $process_instruction:ident, $maximum:expr ) => { + $crate::entrypoint_with_allocator_and_panic_handler!($process_instruction, $maximum); + }; +} + +/// Declare the program entrypoint and set up global handlers. +/// +/// The main difference from the standard (SDK) [`entrypoint`](https://docs.rs/solana-program-entrypoint/latest/solana_program_entrypoint/macro.entrypoint.html) +/// macro is that this macro represents an entrypoint that does not perform allocations or copies +/// when reading the input buffer. /// /// This macro emits the common boilerplate necessary to begin program execution, calling a /// provided function to process the program instruction supplied by the runtime, and reporting /// its result to the runtime. /// -/// It also sets up a [global allocator] and [panic handler], using the [`crate::default_allocator!`] -/// and [`crate::default_panic_handler!`] macros. +/// It also sets up a [global allocator] and [panic handler], using the [`crate::default_allocator`] +/// and [`crate::default_panic_handler`] macros. /// /// The first argument is the name of a function with this type signature: /// @@ -57,7 +76,9 @@ pub const SUCCESS: u64 = super::SUCCESS; /// be ignored. When the maximum is not specified, the default is `64`. This is currently the [maximum /// number of accounts] that a transaction may lock in a block. /// +/// [global allocator]: https://doc.rust-lang.org/stable/alloc/alloc/trait.GlobalAlloc.html /// [maximum number of accounts]: https://github.com/anza-xyz/agave/blob/ccabfcf84921977202fd06d3197cbcea83742133/runtime/src/bank.rs#L3207-L3219 +/// [panic handler]: https://doc.rust-lang.org/stable/core/panic/trait.PanicHandler.html /// /// # Examples /// @@ -70,13 +91,13 @@ pub const SUCCESS: u64 = super::SUCCESS; /// /// use pinocchio::{ /// account_info::AccountInfo, -/// entrypoint, +/// entrypoint_with_allocator_and_panic_handler, /// msg, /// pubkey::Pubkey, /// ProgramResult /// }; /// -/// entrypoint!(process_instruction); +/// entrypoint_with_allocator_and_panic_handler!(process_instruction); /// /// pub fn process_instruction( /// program_id: &Pubkey, @@ -92,19 +113,21 @@ pub const SUCCESS: u64 = super::SUCCESS; /// /// # Important /// -/// The panic handler set up is different dependending on whether the `std` library is available to the -/// linker or not. The `entrypoint!` macro will set up a default panic "hook", that works with the -/// `#[panic_handler]` set by the `std`. Therefore, this macro should be used when the program or any -/// of its dependencies are dependent on the `std` library. +/// The panic handler set up is different depending on whether the `std` library is available to the +/// linker or not. The `entrypoint_with_allocator_and_panic_handler` macro will set up a default +/// panic "hook", that works with the `#[panic_handler]` set by the `std`. Therefore, this macro +/// should be used when the program or any of its dependencies are dependent on the `std` library. /// /// When the program and all its dependencies are `no_std`, it is necessary to set a -/// `#[panic_handler]` to handle panics. This is done by the `nostd_panic_handler!` macro. In this case, -/// it is not possible to use the `entrypoint` macro. Use the `program_entrypoint!` macro instead and set -/// up the allocator and panic handler manually. +/// `#[panic_handler]` to handle panics. This is done by the [`nostd_panic_handler`] macro. In this +/// case, it is not possible to use the `entrypoint_with_allocator_and_panic_handler` macro. Use the +/// [`program_entrypoint`] macro instead and set up the allocator and panic handler manually. #[macro_export] -macro_rules! entrypoint { +macro_rules! entrypoint_with_allocator_and_panic_handler { ( $process_instruction:ident ) => { - entrypoint!($process_instruction, { $crate::MAX_TX_ACCOUNTS }); + entrypoint_with_allocator_and_panic_handler!($process_instruction, { + $crate::MAX_TX_ACCOUNTS + }); }; ( $process_instruction:ident, $maximum:expr ) => { $crate::program_entrypoint!($process_instruction, $maximum); @@ -115,9 +138,9 @@ macro_rules! entrypoint { /// Declare the program entrypoint. /// -/// This macro is similar to the `entrypoint!` macro, but it does not set up a global allocator -/// nor a panic handler. This is useful when the program will set up its own allocator and panic -/// handler. +/// This macro is similar to the [`entrypoint_with_allocator_and_panic_handler`] macro, but it does +/// not set up a global allocator nor a panic handler. This is useful when the program will set up +/// its own allocator and panic handler. #[macro_export] macro_rules! program_entrypoint { ( $process_instruction:ident ) => { @@ -373,9 +396,9 @@ macro_rules! no_allocator { /// /// This macro sets up a global allocator that denies all dynamic allocations, while /// allowing static ("manual") allocations. This is useful when the program does not need to -/// synamically allocate memory and manages their own allocations. +/// dynamically allocate memory and manages their own allocations. /// -/// The program will panic if it tries to allocate memory. +/// The program will panic if it tries to dynamically allocate memory. /// /// This is used when the `"std"` feature is disabled. #[cfg(not(feature = "std"))] @@ -452,7 +475,7 @@ mod alloc { } /// Integer arithmetic in this global allocator implementation is safe when - /// operating on the prescribed `HEAP_START_ADDRESS` and `HEAP_LENGTH`. Any + /// operating on the prescribed [`HEAP_START_ADDRESS`] and [`HEAP_LENGTH`]. Any /// other use may overflow and is thus unsupported and at one's own risk. #[allow(clippy::arithmetic_side_effects)] unsafe impl alloc::alloc::GlobalAlloc for BumpAllocator { From 53d46805a362279d3f0154776273c21ece55d9e1 Mon Sep 17 00:00:00 2001 From: febo Date: Wed, 23 Apr 2025 02:17:45 +0100 Subject: [PATCH 4/4] Fix link --- sdk/pinocchio/src/entrypoint/mod.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/sdk/pinocchio/src/entrypoint/mod.rs b/sdk/pinocchio/src/entrypoint/mod.rs index dc4efd81a..7716881ba 100644 --- a/sdk/pinocchio/src/entrypoint/mod.rs +++ b/sdk/pinocchio/src/entrypoint/mod.rs @@ -33,7 +33,7 @@ pub const SUCCESS: u64 = super::SUCCESS; /// Declare the program entrypoint and set up global handlers. /// /// This macro is deprecated and will be removed in a future version. It is recommended to -/// use the [`entrypoint_with_allocator_and_panic_handler`] macro instead. +/// use the [`crate::entrypoint_with_allocator_and_panic_handler`] macro instead. #[deprecated( since = "0.8.4", note = "Use the `entrypoint_with_allocator_and_panic_handler` macro instead" @@ -119,9 +119,10 @@ macro_rules! entrypoint { /// should be used when the program or any of its dependencies are dependent on the `std` library. /// /// When the program and all its dependencies are `no_std`, it is necessary to set a -/// `#[panic_handler]` to handle panics. This is done by the [`nostd_panic_handler`] macro. In this -/// case, it is not possible to use the `entrypoint_with_allocator_and_panic_handler` macro. Use the -/// [`program_entrypoint`] macro instead and set up the allocator and panic handler manually. +/// `#[panic_handler]` to handle panics. This is done by the [`crate::nostd_panic_handler`](https://docs.rs/pinocchio/latest/pinocchio/macro.nostd_panic_handler.html) +/// macro. In this case, it is not possible to use the `entrypoint_with_allocator_and_panic_handler` +/// macro. Use the [`crate::program_entrypoint`] macro instead and set up the allocator and panic +/// handler manually. #[macro_export] macro_rules! entrypoint_with_allocator_and_panic_handler { ( $process_instruction:ident ) => { @@ -138,7 +139,7 @@ macro_rules! entrypoint_with_allocator_and_panic_handler { /// Declare the program entrypoint. /// -/// This macro is similar to the [`entrypoint_with_allocator_and_panic_handler`] macro, but it does +/// This macro is similar to the [`crate::entrypoint_with_allocator_and_panic_handler`] macro, but it does /// not set up a global allocator nor a panic handler. This is useful when the program will set up /// its own allocator and panic handler. #[macro_export]