diff --git a/sdk/program/src/entrypoint.rs b/sdk/program/src/entrypoint.rs index b266dbf550..b530f7962c 100644 --- a/sdk/program/src/entrypoint.rs +++ b/sdk/program/src/entrypoint.rs @@ -32,12 +32,85 @@ pub const HEAP_START_ADDRESS: usize = 0x300000000; /// Length of the heap memory region used for program heap. pub const HEAP_LENGTH: usize = 32 * 1024; -/// Declare the entry point of the program and use the default local heap -/// implementation +/// Declare the program entry point and set up global handlers. /// -/// Deserialize the program input arguments and call the user defined -/// `process_instruction` function. Users must call this macro otherwise an -/// entry point for their program will not be created. +/// 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 +/// [`custom_heap_default`] and [`custom_panic_default`] macros. +/// +/// [global allocator]: https://doc.rust-lang.org/stable/std/alloc/trait.GlobalAlloc.html +/// [panic handler]: https://doc.rust-lang.org/nomicon/panic-handler.html +/// +/// The argument is the name of a function with this type signature: +/// +/// ```ignore +/// fn process_instruction( +/// program_id: &Pubkey, // Public key of the account the program was loaded into +/// accounts: &[AccountInfo], // All accounts required to process the instruction +/// instruction_data: &[u8], // Serialized instruction-specific data +/// ) -> ProgramResult; +/// ``` +/// +/// # Cargo features +/// +/// This macro emits symbols and definitions that may only be defined once +/// globally. As such, if linked to other Rust crates it will cause compiler +/// errors. To avoid this, it is common for Solana programs to define an +/// optional [Cargo feature] called `no-entrypoint`, and use it to conditionally +/// disable the `entrypoint` macro invocation, as well as the +/// `process_instruction` function. See a typical pattern for this in the +/// example below. +/// +/// [Cargo feature]: https://doc.rust-lang.org/cargo/reference/features.html +/// +/// The code emitted by this macro can be customized by adding cargo features +/// _to your own crate_ (the one that calls this macro) and enabling them: +/// +/// - If the `custom-heap` feature is defined then the macro will not set up the +/// global allocator, allowing `entrypoint` to be used with your own +/// allocator. See documentation for the [`custom_heap_default`] macro for +/// details of customizing the global allocator. +/// +/// - If the `custom-panic` feature is defined then the macro will not define a +/// panic handler, allowing `entrypoint` to be used with your own panic +/// handler. See documentation for the [`custom_panic_default`] macro for +/// details of customizing the panic handler. +/// +/// # Examples +/// +/// Defining an entry point and making it conditional on the `no-entrypoint` +/// feature. Although the `entrypoint` module is written inline in this example, +/// it is common to put it into its own file. +/// +/// ```no_run +/// #[cfg(not(feature = "no-entrypoint"))] +/// pub mod entrypoint { +/// +/// use solana_program::{ +/// account_info::AccountInfo, +/// entrypoint, +/// entrypoint::ProgramResult, +/// msg, +/// pubkey::Pubkey, +/// }; +/// +/// entrypoint!(process_instruction); +/// +/// pub fn process_instruction( +/// program_id: &Pubkey, +/// accounts: &[AccountInfo], +/// instruction_data: &[u8], +/// ) -> ProgramResult { +/// msg!("Hello world"); +/// +/// Ok(()) +/// } +/// +/// } +/// ``` #[macro_export] macro_rules! entrypoint { ($process_instruction:ident) => { @@ -56,17 +129,28 @@ macro_rules! entrypoint { }; } -/// Fallback to default for unused custom heap feature. +/// Define the default global allocator. +/// +/// The default global allocator is enabled only if the calling crate has not +/// disabled it using [Cargo features] as described below. It is only defined +/// for [BPF] targets. +/// +/// [Cargo features]: https://doc.rust-lang.org/cargo/reference/features.html +/// [BPF]: https://docs.solana.com/developing/on-chain-programs/overview#berkeley-packet-filter-bpf +/// +/// # Cargo features +/// +/// A crate that calls this macro can provide its own custom heap +/// implementation, or allow others to provide their own custom heap +/// implementation, by adding a `custom-heap` feature to its `Cargo.toml`. After +/// enabling the feature, one may define their own [global allocator] in the +/// standard way. +/// +/// [global allocator]: https://doc.rust-lang.org/stable/std/alloc/trait.GlobalAlloc.html +/// #[macro_export] macro_rules! custom_heap_default { () => { - /// A program can provide their own custom heap implementation by adding - /// a `custom-heap` feature to `Cargo.toml` and implementing their own - /// `global_allocator`. - /// - /// If the program defines the feature `custom-heap` then the default heap - /// implementation will not be included and the program is free to implement - /// their own `#[global_allocator]` #[cfg(all(not(feature = "custom-heap"), target_arch = "bpf"))] #[global_allocator] static A: $crate::entrypoint::BumpAllocator = $crate::entrypoint::BumpAllocator { @@ -76,19 +160,53 @@ macro_rules! custom_heap_default { }; } -/// Fallback to default for unused custom panic feature. -/// This must be used if the entrypoint! macro is not used. +/// Define the default global panic handler. +/// +/// This must be used if the [`entrypoint`] macro is not used, and no other +/// panic handler has been defined; otherwise compilation will fail with a +/// missing `custom_panic` symbol. +/// +/// The default global allocator is enabled only if the calling crate has not +/// disabled it using [Cargo features] as described below. It is only defined +/// for [BPF] targets. +/// +/// [Cargo features]: https://doc.rust-lang.org/cargo/reference/features.html +/// [BPF]: https://docs.solana.com/developing/on-chain-programs/overview#berkeley-packet-filter-bpf +/// +/// # Cargo features +/// +/// A crate that calls this macro can provide its own custom panic handler, or +/// allow others to provide their own custom panic handler, by adding a +/// `custom-panic` feature to its `Cargo.toml`. After enabling the feature, one +/// may define their own panic handler. +/// +/// A good way to reduce the final size of the program is to provide a +/// `custom_panic` implementation that does nothing. Doing so will cut ~25kb +/// from a noop program. That number goes down the more the programs pulls in +/// Rust's standard library for other purposes. +/// +/// # Defining a panic handler for Solana +/// +/// _The mechanism for defining a Solana panic handler is different [from most +/// Rust programs][rpanic]._ +/// +/// [rpanic]: https://doc.rust-lang.org/nomicon/panic-handler.html +/// +/// To define a panic handler one must define a `custom_panic` function +/// with the `#[no_mangle]` attribute, as below: +/// +/// ```ignore +/// #[cfg(all(feature = "custom-panic", target_arch = "bpf"))] +/// #[no_mangle] +/// fn custom_panic(info: &core::panic::PanicInfo<'_>) { +/// $crate::msg!("{}", info); +/// } +/// ``` +/// +/// The above is how Solana defines the default panic handler. #[macro_export] macro_rules! custom_panic_default { () => { - /// A program can provide their own custom panic implementation by - /// adding a `custom-panic` feature to `Cargo.toml` and implementing - /// their own `custom_panic`. - /// - /// A good way to reduce the final size of the program is to provide a - /// `custom_panic` implementation that does nothing. Doing so will cut - /// ~25kb from a noop program. That number goes down the more the - /// programs pulls in Rust's libstd for other purposes. #[cfg(all(not(feature = "custom-panic"), target_arch = "bpf"))] #[no_mangle] fn custom_panic(info: &core::panic::PanicInfo<'_>) {