Repository: nervosnetwork/ckb-std (language: rust)

Issue:
Wrong argc/argv in debug target
Original discussion: https://github.com/nervosnetwork/capsule/issues/72


Discussion / hints:

Fix patch:
diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml
index a00a264..b55c4b9 100644
--- a/.github/workflows/rust.yml
+++ b/.github/workflows/rust.yml
@@ -18,6 +18,6 @@ jobs:
     - name: Run integration
       run: make integration-in-docker
     - name: Install capsule
-      run: cargo install ckb-capsule
+      run: cargo install ckb-capsule --version 0.8.0
     - name: Run tests
       run: make test
diff --git a/capsule.toml b/capsule.toml
index 9b5afb9..eddbc25 100644
--- a/capsule.toml
+++ b/capsule.toml
@@ -5,7 +5,7 @@ toolchain = "nightly-2022-08-01"
 docker_image = "thewawar/ckb-capsule:2022-08-01"
 
 # capsule version
-version = "0.7.3 96bf4af"
+version = "0.8.0"
 # path of deployment config file
 deployment = "deployment.toml"
 
diff --git a/contracts/exec-callee/src/entry.rs b/contracts/exec-callee/src/entry.rs
index 5b454d1..10edd50 100644
--- a/contracts/exec-callee/src/entry.rs
+++ b/contracts/exec-callee/src/entry.rs
@@ -3,26 +3,22 @@ use core::result::Result;
 
 // Import CKB syscalls and structures
 // https://nervosnetwork.github.io/ckb-std/riscv64imac-unknown-none-elf/doc/ckb_std/index.html
-use ckb_std::cstr_core::{cstr, CStr};
+use core::ffi::CStr;
 use core::slice::from_raw_parts;
 
 use crate::error::Error;
 
-pub fn main(argc: u64, argv: *const *const u8) -> Result<(), Error> {
-    let args = unsafe { from_raw_parts(argv, argc as usize) };
+pub unsafe fn main(
+    argc: core::ffi::c_int,
+    argv: *const *const core::ffi::c_char,
+) -> Result<(), Error> {
+    let args = from_raw_parts(argv, argc as usize);
 
-    #[cfg(target_arch = "riscv64")]
-    let arg1 = unsafe { CStr::from_ptr(args[0]) };
-    #[cfg(not(target_arch = "riscv64"))]
-    let arg1 = unsafe { CStr::from_ptr(args[0] as *const i8) };
-
-    #[cfg(target_arch = "riscv64")]
-    let arg2 = unsafe { CStr::from_ptr(args[1]) };
-    #[cfg(not(target_arch = "riscv64"))]
-    let arg2 = unsafe { CStr::from_ptr(args[1] as *const i8) };
+    let arg1 = CStr::from_ptr(args[0]);
+    let arg2 = CStr::from_ptr(args[1]);
 
     assert_eq!(argc, 2);
-    assert_eq!(arg1, cstr!("Hello World"));
-    assert_eq!(arg2, cstr!("你好"));
+    assert_eq!(arg1.to_bytes(), b"Hello World");
+    assert_eq!(arg2.to_bytes(), "你好".as_bytes());
     Ok(())
 }
diff --git a/contracts/exec-callee/src/main.rs b/contracts/exec-callee/src/main.rs
index d507a4b..819dae1 100644
--- a/contracts/exec-callee/src/main.rs
+++ b/contracts/exec-callee/src/main.rs
@@ -16,15 +16,12 @@ mod entry;
 mod error;
 
 use ckb_std::default_alloc;
-use core::arch::asm;
 
 ckb_std::entry!(program_entry);
 default_alloc!();
 
 /// program entry
-///
-///  Both `argc` and `argv` can be omitted.
-fn program_entry(argc: u64, argv: *const *const u8) -> i8 {
+unsafe extern "C" fn program_entry(argc: core::ffi::c_int, argv: *const *const core::ffi::c_char) -> i8 {
     // Call main function and return error code
     match entry::main(argc, argv) {
         Ok(_) => 0,
diff --git a/contracts/exec-caller-by-code-hash/src/entry.rs b/contracts/exec-caller-by-code-hash/src/entry.rs
index 44401a6..7f9865b 100644
--- a/contracts/exec-caller-by-code-hash/src/entry.rs
+++ b/contracts/exec-caller-by-code-hash/src/entry.rs
@@ -11,12 +11,11 @@ pub fn main() -> Result<(), Error> {
     let arg1 = cstr!("Hello World");
     let arg2 = cstr!("你好");
     // $ ckb-cli util blake2b --binary-path build/debug/exec-callee
-    // 0x27ce272a3b51f2cd8bc684a9e16ae0ee8021710dcf370a14c592da9cb910efb7
-
+    // 0xad1a71e71353b2439c0e4d7d634f0cdb2c56faad755500bed07cbe76a71e6000
     let code_hash = [
-        0x27, 0xce, 0x27, 0x2a, 0x3b, 0x51, 0xf2, 0xcd, 0x8b, 0xc6, 0x84, 0xa9, 0xe1, 0x6a, 0xe0,
-        0xee, 0x80, 0x21, 0x71, 0x0d, 0xcf, 0x37, 0x0a, 0x14, 0xc5, 0x92, 0xda, 0x9c, 0xb9, 0x10,
-        0xef, 0xb7,
+        0xad, 0x1a, 0x71, 0xe7, 0x13, 0x53, 0xb2, 0x43, 0x9c, 0x0e, 0x4d, 0x7d, 0x63, 0x4f, 0x0c,
+        0xdb, 0x2c, 0x56, 0xfa, 0xad, 0x75, 0x55, 0x00, 0xbe, 0xd0, 0x7c, 0xbe, 0x76, 0xa7, 0x1e,
+        0x60, 0x00,
     ];
     ckb_std::debug!("code_hash: {:?}", code_hash);
     let ret = high_level::exec_cell(
diff --git a/contracts/exec-caller-by-code-hash/src/main.rs b/contracts/exec-caller-by-code-hash/src/main.rs
index b6e9c94..e01b44c 100644
--- a/contracts/exec-caller-by-code-hash/src/main.rs
+++ b/contracts/exec-caller-by-code-hash/src/main.rs
@@ -16,15 +16,12 @@ mod entry;
 mod error;
 
 use ckb_std::default_alloc;
-use core::arch::asm;
 
 ckb_std::entry!(program_entry);
 default_alloc!();
 
 /// program entry
-///
-///  Both `argc` and `argv` can be omitted.
-fn program_entry(_argc: u64, _argv: *const *const u8) -> i8 {
+extern "C" fn program_entry() -> i8 {
     // Call main function and return error code
     match entry::main() {
         Ok(_) => 0,
diff --git a/contracts/exec-caller/src/main.rs b/contracts/exec-caller/src/main.rs
index b6e9c94..e01b44c 100644
--- a/contracts/exec-caller/src/main.rs
+++ b/contracts/exec-caller/src/main.rs
@@ -16,15 +16,12 @@ mod entry;
 mod error;
 
 use ckb_std::default_alloc;
-use core::arch::asm;
 
 ckb_std::entry!(program_entry);
 default_alloc!();
 
 /// program entry
-///
-///  Both `argc` and `argv` can be omitted.
-fn program_entry(_argc: u64, _argv: *const *const u8) -> i8 {
+extern "C" fn program_entry() -> i8 {
     // Call main function and return error code
     match entry::main() {
         Ok(_) => 0,
diff --git a/examples/main.rs b/examples/main.rs
index f5fdb75..7d5888d 100644
--- a/examples/main.rs
+++ b/examples/main.rs
@@ -7,10 +7,9 @@
 
 use alloc::vec;
 use ckb_std::{debug, default_alloc, entry};
-use core::arch::asm;
 
 #[no_mangle]
-pub fn main() -> i8 {
+extern "C" fn main() -> i8 {
     let v = vec![0u8; 42];
     debug!("{:?}", v.len());
     0
diff --git a/src/entry.rs b/src/entry.rs
index 7a03887..6f1b64c 100644
--- a/src/entry.rs
+++ b/src/entry.rs
@@ -1,11 +1,28 @@
-/// Define entry point
+/// Define program entry point (`_start` function) and lang items (panic handler, oom handler, etc.).
 ///
+/// # Examples
 ///
-/// # Example
+/// Simple main function:
 ///
 /// ```
-/// // define contract entry point
 /// entry!(main)
+///
+/// extern "C" fn main() -> i8 {
+///    0
+/// }
+/// ```
+///
+/// Reading exec args:
+///
+/// ```
+/// entry!(main)
+///
+/// unsafe extern "C" fn main(argc: core::ffi::c_int, argv: *const *const core::ffi::c_char) -> i8 {
+///    let args = core::slice::from_raw_parts(argv, argc as usize);
+///    let args: alloc::vec::Vec<_> = args.iter().map(|arg| CStr::from_ptr(*arg)).collect();
+///    debug!("argc: {}, args: {:?}", argc, args);
+///    0
+/// }
 /// ```
 #[macro_export]
 macro_rules! entry {
@@ -17,25 +34,30 @@ macro_rules! entry {
             panic!("Out of memory")
         }
 
+        #[cfg(not(target_arch = "riscv64"))]
         #[no_mangle]
         pub extern "C" fn _start() -> ! {
-            #[cfg(target_arch = "riscv64")]
-            unsafe {
-                asm!(
-                    "lw a0, 0(sp)",
-                    "addi a1, sp, 8",
-                    "li a2, 0",
-                    "call {}",
-                    "li a7, 93",
-                    "ecall",
-                    sym $main,
-                    options(noreturn)
-                );
-            }
-            #[cfg(not(target_arch = "riscv64"))]
-            panic!("This macro is only valid for riscv64 target");
+            panic!("ckb_std::entry is only valid for riscv64 target")
         }
 
+        // Use global_asm so the compiler won't insert function prologue in _start.
+        #[cfg(target_arch = "riscv64")]
+        core::arch::global_asm!(
+            ".global _start",
+            "_start:",
+            // Argc.
+            "lw a0, 0(sp)",
+            // Argv.
+            "addi a1, sp, 8",
+            // Envp.
+            "li a2, 0",
+            "call {}",
+            // Exit.
+            "li a7, 93",
+            "ecall",
+            sym $main
+        );
+
         #[lang = "eh_personality"]
         extern "C" fn eh_personality() {}
 


Test patch:
diff --git a/contracts/ckb-std-tests/src/main.rs b/contracts/ckb-std-tests/src/main.rs
index d4044c5..8b62da1 100644
--- a/contracts/ckb-std-tests/src/main.rs
+++ b/contracts/ckb-std-tests/src/main.rs
@@ -17,15 +17,12 @@ mod entry;
 mod error;
 
 use ckb_std::default_alloc;
-use core::arch::asm;
 
 ckb_std::entry!(program_entry);
 default_alloc!();
 
 /// program entry
-///
-///  Both `argc` and `argv` can be omitted.
-fn program_entry(_argc: u64, _argv: *const *const u8) -> i8 {
+extern "C" fn program_entry() -> i8 {
     // Call main function and return error code
     match entry::main() {
         Ok(_) => 0,
diff --git a/test/simulator/src/exec_callee.rs b/test/simulator/src/exec_callee.rs
index cc3a46d..030b2b2 100644
--- a/test/simulator/src/exec_callee.rs
+++ b/test/simulator/src/exec_callee.rs
@@ -39,14 +39,14 @@ fn main() {
         .into_iter()
         .map(|arg| CString::new(arg.into_vec()).unwrap())
         .collect::<Vec<_>>();
-    let argc = args.len() as u64;
+    let argc = args.len() as core::ffi::c_int;
     let mut argv = args
         .iter()
-        .map(|cstring| cstring.to_bytes_with_nul().as_ptr())
+        .map(|cstring| cstring.as_ptr())
         .collect::<Vec<_>>();
     argv.push(std::ptr::null());
     println!("START simulator callee entry");
-    let code = entry::main(argc, argv.as_ptr())
+    let code = unsafe { entry::main(argc, argv.as_ptr()) }
         .map(|()| 0i32)
         .unwrap_or_else(|err| err as i32);
     if code != 0 {