Home > Blockchain >  Problem with calling C function that receive command line arguments from Rust
Problem with calling C function that receive command line arguments from Rust

Time:10-05

I am trying to call a C function from rust. The function suppose to receive the command lines arguments then print it. I used cmake to compile the C code to a static archive. I write a build.rs script to referee to the static library location and to make the static linking to it.

// library.cpp

#include "library.h"
#include <iostream>

extern "C"{
    void print_args(int argc, char *argv[]){
        std::cout << "Have " << argc << " arguments:" << std::endl;
        std::cout<<argv<<std::endl;
        for (int i = 0; i < argc;   i) {
            std::cout << argv[i] << std::endl;
        }
    }
} 
//library.h 
extern "C"{
    void print_args(int argc, char *argv[]);
}
//build.rs
pub fn main(){
    println!("cargo:rustc-link-search=.../cmake-build-debug");  //library.a directory 
    println!("cargo:rustc-link-lib=static=stdc  ");
    println!("cargo:rustc-link-lib=static=library");
}

//main.rs
#[link(name = "library", kind = "static")]
extern "C" {
    pub fn print_args(args: c_int, argsv: *const c_char);
}

fn main() {
    let args = std::env::args()
        .map(|arg| CString::new(arg).unwrap())
        .collect::<Vec<CString>>();
    let args_len: c_int = args.len() as c_int;
    let c_args_ptr = args.as_ptr() as *const c_char;
    unsafe { print_args(args_len, c_args_ptr) };
}

When running the rust code by the command cargo run "10" "11" . it is only able to print the first argument which is the name of the program then the error error: process didn't exit successfully: target\debug\static_library_binding_test.exe 10 11 (exit code: 0xc0000005, STATUS_ACCESS_VIOLATION) appears.

it is the output rust main.rs

Have 3 arguments:
target\debug\static_library_binding_test.exe
error: process didn't exit successfully: `target\debug\static_library_binding_test.exe 10 11` (exit code: 0xc0000005, STATUS_ACCESS_VIOLATION)

So, I need to know how can I pass the command line argument from rust to the c function.

CodePudding user response:

The problem is in this code:

let args = std::env::args()
    .map(|arg| CString::new(arg).unwrap())
    .collect::<Vec<CString>>();
// ...
let c_args_ptr = args.as_ptr() as *const c_char;

That creates a vector of CString objects, which you then proceed to cast into a pointer. A CString consists of two word-sized values, a pointer and a length, and cannot be interpreted as just a pointer. To get an actual array of pointers which print_args() expects, you need to collect them separately:

let args = std::env::args()
    .map(|arg| CString::new(arg).unwrap())
    .collect::<Vec<CString>>();
let arg_ptrs: Vec<*const c_char> = args.iter().map(|s| s.as_ptr()).collect();
let args_len: c_int = args.len() as c_int;
unsafe { print_args(args_len, arg_ptrs.as_ptr()) };

Note that you'll need to declare print_args as taking pointer to pointer:

#[link(name = "library", kind = "static")]
extern "C" {
    pub fn print_args(args: c_int, argsv: *const *const c_char);
}
  • Related