Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix panic_handler #84

Merged
merged 3 commits into from
Mar 15, 2025
Merged

fix panic_handler #84

merged 3 commits into from
Mar 15, 2025

Conversation

publicqi
Copy link
Contributor

@publicqi publicqi commented Mar 3, 2025

#![no_std]
use pinocchio::{
    account_info::AccountInfo, default_allocator, default_panic_handler, program_entrypoint,
    pubkey::Pubkey, ProgramResult,
};

program_entrypoint!(process_instruction);
default_allocator!();
default_panic_handler!();

fn process_instruction(_: &Pubkey, _: &[AccountInfo], _: &[u8]) -> ProgramResult {
    Ok(())
}
[package]
name = "pinocchio-test"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib", "lib"]

[dependencies]
pinocchio = { version = "0.7.1" }

[profile.release]
# strip = "symbols"
lto = true
opt-level = 3

This code doesn't compile now. Error message is

error: `#[panic_handler]` function required, but not found

@febo
Copy link
Collaborator

febo commented Mar 9, 2025

Did you try compiling without being #![no_std]? I think when you link the std, this change might conflict.

@febo
Copy link
Collaborator

febo commented Mar 10, 2025

Perhaps instead of modifying the existing macro, we need to add one specific for no_std:

#[macro_export]
macro_rules! nostd_panic_handler {
    () => {
        /// A panic handler for `no_std`.
        #[cfg(not(test))]
        #[no_mangle]
        #[panic_handler]
        fn custom_panic(info: &core::panic::PanicInfo<'_>) -> ! {
            if let Some(location) = info.location() {
                $crate::log::sol_log(location.file());
            }
            // Panic reporting.
            $crate::log::sol_log("** PANICKED **");
            // Never returns.
            loop {}
        }
    };
}

@publicqi
Copy link
Contributor Author

yeah that sounds good.

@publicqi publicqi changed the title specify #[panic_handler] for default_panic_handler fix panic_handler Mar 10, 2025
@publicqi publicqi marked this pull request as draft March 10, 2025 08:58
@publicqi publicqi force-pushed the panic-handler branch 2 times, most recently from 75f4f99 to 4159cb3 Compare March 10, 2025 09:33
@publicqi
Copy link
Contributor Author

publicqi commented Mar 10, 2025

i had a wrong understanding about how panic works in sbf and now it's corrected:

std:
when a panic happens, rust runtime will call

rust_begin_unwind (std generted)
    std::sys::pal::sbf::panic (sbf compiler generated)
        custom_panic (pinocchio)
        abort syscall (sbf compiler generated)

no_std:

rust_begin_unwind (pinocchio)

so when using std, we define custom_panic as a hook and abort will be called eventually. but in no_std, we will need to manually implement rust_begin_unwind (or i should rephrase it "[panic_handler] will be compiled to rust_begin_unwind").

@publicqi publicqi marked this pull request as ready for review March 10, 2025 09:39
@@ -252,12 +254,24 @@ macro_rules! default_panic_handler {
/// Default panic handler.
#[cfg(all(not(feature = "custom-panic"), target_os = "solana"))]
#[no_mangle]
fn custom_panic(info: &core::panic::PanicInfo<'_>) {
#[panic_handler]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's remove the #[panic_handler] attribute from here. The default_panic_handler will be used when you import pinocchio as a no_std but your program links against the std.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so four cases here:

  • if program is std, they'll need to manually specify features = ["std"] in their Cargo.toml. in that case custom-panic (using msg) will be used.
  • if a program is no_std, rustc compiler will ask them to specify a [panic_handler], which is what handler doing.
  • if a program is no_std but specifying features = ["std"], this should be undefined behavior (e.g. format in pinocchio won't work anyways)
  • if a program is std while not specifying features = ["std"], this is kind of shady.

to conclude,

  • with this [panic_handler], we discourage the fourth case as std will generate a builtin [panic_handler] and they will conflict.
  • without this [panic_handler], people cannot use entrypoint!. they need to use program_entrypoint! and add a [panic_handler] themself.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Few comments:

  • in the first case, you could still have pinocchio as "no_std" even though your program is std. That would mean pinocchio won't use anything from the std but your program might.
  • third case probably will fail compilation, since std won't be available.
  • fourth case is not an ideal scenario, but it could happen quite often. The benefit of having pinocchio as a "no_std" is that it won't bring all the format! stuff automatically (reduces the binary size), but you might still have a library in your program that requires std.

@febo
Copy link
Collaborator

febo commented Mar 11, 2025

Let's add a specific opt-in nostd panic handler:

#[macro_export]
macro_rules! nostd_panic_handler {
    () => {
        /// A panic handler for `no_std`.
        #[cfg(not(test))]
        #[no_mangle]
        #[panic_handler]
        fn handler(info: &core::panic::PanicInfo<'_>) -> ! {
            if let Some(location) = info.location() {
                unsafe {
                    $crate::syscalls::sol_panic_(
                        location.file().as_ptr(),
                        location.file().len() as u64,
                        location.line() as u64,
                        location.column() as u64,
                    )
                }
            } else {
                $crate::log::sol_log("** PANICKED **");
                unsafe { $crate::syscalls::abort() }
            }
        }
    };
}

@febo
Copy link
Collaborator

febo commented Mar 11, 2025

When your program is no_std:

program_entrypoint!(...);
no_allocator!();
nostd_panic_handler!();

When your program links against the std, then you can use either of the default_panic_handler - it will depend on whether you are importing pinocchio as no_std or not:

entrypoint(...);

@publicqi
Copy link
Contributor Author

When your program links against the std, then you can use either of the default_panic_handler - it will depend on whether you are importing pinocchio as no_std or not:

the functionality difference between the two is only CU diff and format? can we just deprecate the std custom-panic?

@febo
Copy link
Collaborator

febo commented Mar 12, 2025

When your program links against the std, then you can use either of the default_panic_handler - it will depend on whether you are importing pinocchio as no_std or not:

the functionality difference between the two is only CU diff and format? can we just deprecate the std custom-panic?

Soon we can deprecate it. At the moment the one with the format! is able to give the dynamic panic message, while the "no_std" variation of the default_panic_handler does not.

In rust 1.84, fmt::Arguments::as_str will be stabilized (see rust-lang/rust#132511), then we can have a single one that does not require std.

We will be able to use:

if let Some(Some(mm)) = info.message().map(|mes| mes.as_str()) {
     let mes = mm.as_bytes();
     unsafe {
          $crate::syscalls::sol_log_(mes.as_ptr(), mes.len() as u64);
     }
}

@febo
Copy link
Collaborator

febo commented Mar 14, 2025

@publicqi Wondering if you had a chance to look into my last comment. I am preparing a release and ideally would like to include this fix.

@publicqi
Copy link
Contributor Author

yes. i'll fix it

@publicqi
Copy link
Contributor Author

@febo ready for review. updated some docs too so hopefully it's clear enough.

@publicqi
Copy link
Contributor Author

force pushed to clean the messy commits

@febo
Copy link
Collaborator

febo commented Mar 15, 2025

force pushed to clean the messy commits

Looks great – just a nit: let's remove the feature = "custom-panic references, they might have sneaked in when you rebased.

@publicqi
Copy link
Contributor Author

good catch. fixed.

@febo
Copy link
Collaborator

febo commented Mar 15, 2025

One more thing, I think the changes to the syscalls module are also missing.

@publicqi
Copy link
Contributor Author

ahh yes...

@publicqi
Copy link
Contributor Author

Copy link
Collaborator

@febo febo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great – thanks!

@febo febo merged commit 94872e6 into anza-xyz:main Mar 15, 2025
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants