Skip to main content

Main & Allocator

note

These two macros are explained together because they are both global settings that must be run before _start. In practice, they are usually used together (typically auto-generated by ckb-script-template).

In the previous chapter, a Script does not rely on an OS, so both no_std and no_main must be enabled:

  • no_std: Disables the standard library.
  • no_main: Since the standard library’s entry point is unavailable, the _start function must be defined manually.

At the same time, common Rust types like String and Vec depend on alloc, but in no_std, there is no default memory allocator. Therefore, developers must configure it manually.

Here’s a minimal example:

#![no_std]
#![no_main]
extern crate alloc;
ckb_std::default_alloc!(16384, 1258306, 64);
ckb_std::entry!(program_entry);
pub fn program_entry() -> i8 {
ckb_std::debug!("Hello World!");
0
}

(See rust-script-examples/hello-world for reference.)

entry!

Define program entry point (_start function) and lang items (panic handler, etc.).

Syntax

macro_rules! entry {
($main:path) => { ... }
}

Parameters

  • main: Name of the main function. It must be defined as: pub fn FunctionName() -> i8.

Return

None

Remarks

The Script must explicitly define the _start function. A typical implementation:

#![no_std]
#![no_main]
core::arch::global_asm!(
".global _start",
"_start:",
// Argc.
"lw a0, 0(sp)",
// Argv.
"addi a1, sp, 8",
// Envp.
"li a2, 0",
"call __ckb_std_main",
// Exit.
"li a7, 93",
"ecall",
);

#[no_mangle]
unsafe extern "C" fn __ckb_std_main(
_argc: core::ffi::c_int,
_argv: *const core::ffi::c_void,
) -> i8 {
0
}

Explanation:

  • Embeds asm code with global_asm!.
  • .global _start set _start as the program entry.
  • Loads arguments from the stack into registers a0 and a1 (mainly for subprocesses created via exec/spawn, usually unnecessary for Scripts).
  • Clears register a2.
  • Calls __ckb_std_main.
note
  • __ckb_std_main must use C style (extern "C").
  • It must be marked with #[no_mangle] to prevent the compiler from altering the symbol.

Internally, the entry! macro calls __ckb_std_main and then executes the provided main function (e.g., program_entry).

default_alloc!

Defines global allocator.

Syntax

macro_rules! default_alloc {
() => {
$crate::default_alloc!({ 4 * 1024 }, { 516 * 1024 }, 64);
};
($fixed_block_heap_size:expr, $heap_size:expr, $min_block_size:expr) => {
...
}

Parameters

If None parameters: defaults to: default_alloc!({ 4 * 1024 }, { 516 * 1024 }, 64).

Else:

  • fixed_block_heap_size: Size of the fixed block heap.
  • heap_size: Size of the heap.
  • min_block_size: The min size to allocate.
note

We recommend: the following heap configuration is used: 16KB fixed heap, 1.2MB(rounded up to be 16-byte aligned) dynamic heap. Minimal memory block in dynamic heap is 64 bytes.

ckb_std::default_alloc!(16384, 1258306, 64);

Return

None

Remarks

In the example above, ckb_std::debug! relies on alloc::string, so you need:

extern crate alloc;
ckb_std::default_alloc!();

Here’s a simple example of how default_alloc could be implemented:

#[global_allocator]
static ALLOC: buddy_alloc::NonThreadsafeAlloc = unsafe {
#[repr(align(64))]
struct _AlignedHeap<const N: usize>([u8; N]);

const FIXED_BLOCK_HEAP_SIZE: usize = 4 * 1024;
const HEAP_SIZE: usize = 516 * 1024;
const MIN_BLOCK_SIZE: usize = 64;

static mut _BUDDY_HEAP: _AlignedHeap<HEAP_SIZE> = _AlignedHeap([0u8; HEAP_SIZE]);
static mut _FIXED_BLOCK_HEAP: _AlignedHeap<FIXED_BLOCK_HEAP_SIZE> =
_AlignedHeap([0u8; FIXED_BLOCK_HEAP_SIZE]);

use buddy_alloc::{BuddyAllocParam, FastAllocParam, NonThreadsafeAlloc};
#[allow(static_mut_refs)]
NonThreadsafeAlloc::new(
FastAllocParam::new(_FIXED_BLOCK_HEAP.0.as_ptr(), FIXED_BLOCK_HEAP_SIZE),
BuddyAllocParam::new_with_zero_filled(
_BUDDY_HEAP.0.as_ptr(),
HEAP_SIZE,
MIN_BLOCK_SIZE,
),
)
};

Explanation:

  • #[global_allocator] declares the global allocator.
  • Uses buddy_alloc as the memory allocation (the same allocator used internally by ckb-std).

example

In rust-script-examples/empty, implement a basic Rust Script following the structure described above. This example uses the allocator feature to create a heap allocator, enabling the use of types like String through alloc.