Failed to invoke functions when running wasm file with wasmtime - c

Mozilla shared WASI and how to use Wasmtime to run .wasm file in their blog post. The programming language they demonstrated is Rust:
#[wasm_bindgen]
pub fn render(input: &str) -> String {
let parser = Parser::new(input);
let mut html_output = String::new();
html::push_html(&mut html_output, parser);
return html_output;
}
However, I want to do the same thing in C.
I've downloaded wasi-libc and tried to build a 'hello world' program with Clang.
I created two functions in test.c:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int foo1()
{
printf("Hello foo1()\n");
return 0;
}
int foo2(char* filename)
{
printf("Hello foo2()\n");
printf("filename is %s\n", filename);
return 0;
}
Build it with the command:
clang --target=wasm32-wasi --sysroot=/mnt/d/code/wasi-libc/sysroot test.c -o test.wasm -nostartfiles -Wl,--no-entry,--export=foo1,--export=foo2
Run the wasm file to invoke functions:
$ wasmtime test.wasm --invoke foo1
Hello foo1()
warning: using `--render` with a function that returns values is experimental and may break in the future
0
$ wasmtime test.wasm --invoke foo2 "hello"
warning: using `--render` with a function that takes arguments is experimental and may break in the future
error: failed to process main module `test.wasm`
caused by: invalid digit found in string
I failed to invoke the function with an input parameter.
What's the difference between Rust and C? Is Rust currently the only way to build wasm lib file?

The difference is that the Rust toolchain has experimental support for Interface Types, whereas that doesn't yet exist for C, unfortunately. The #[wasm_bindgen] above the render function is what turns render into a function exported with Interface Types bindings.

Related

Rust bindgen cannot find platform specific library?

I am trying to port my simple application from C to Rust. It was running only on my Mac, with a library on Mac only. Here is a simplified version of the failed part in C code
// myLog.h
#include <os/log.h> // macOS header
void debug(const char *str);
//************************************
// myLog.c
#include "myLog.h"
void debug(const char* str) {
// call the macOS log function
os_log_debug(OS_LOG_DEFAULT, "%{public}s", str);
}
This code can be compiled simply calling gcc debug.c, and it works fine.
Then I added the .h and .c to my rust project with bindgen specified like below
fn main() {
println!("cargo:rerun-if-changed=myLog.h");
let bindings = bindgen::Builder::default()
.header("myLog.h")
.parse_callbacks(Box::new(bindgen::CargoCallbacks))
.generate()
.expect("Unable to build bindgen");
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
bindings
.write_to_file(out_path.join("mylog_bindings.rs"))
.expect("Couldn't write bindings!");
}
And the main function has no other functions, but testing the log for now:
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
use std::ffi::CString;
include!(concat!(env!("OUT_DIR"), "/mylog_bindings.rs"));
fn main() {
let log_infomation = CString::new("Log from Rust").expect("Failed to create c string");
let c_pointer = log_infomation.as_ptr();
unsafe {
debug(c_pointer);
}
}
The program failed with following error:
error: linking with `cc` failed: exit code: 1
|
= note: "cc" "-m64" "-arch" "x86_64" "-L" ......
= note: Undefined symbols for architecture x86_64:
"_debug", referenced from:
bindgen_test::main::hc0e5702b90adf92c in bindgen_test.3ccmhz8adio5obzw.rcgu.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
error: aborting due to previous error; 2 warnings emitted
error: could not compile `bindgen_test`.
I am not sure why this failed, but I found if I remove the whole unsafe block (without calling the function), the compilation will work. But can someone explain to me what I did wrong? Is there something I need to add to make it compile?
Thank you very much!
The problem is that you are not including the myLog.c file anywhere, only the myLog.h header. This is what bindgen does: it converts a C header file into Rust code, but it does not compile the C code itself.
For that you need the cc crate. You have to use both cc and bindgen together in your build.rs file:
use std::env;
use std::path::PathBuf;
fn main() {
println!("cargo:rerun-if-changed=myLog.h");
println!("cargo:rerun-if-changed=myLog.c"); // new line here!!
let bindings = bindgen::Builder::default()
.header("myLog.h")
.parse_callbacks(Box::new(bindgen::CargoCallbacks))
.generate()
.expect("Unable to build bindgen");
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
bindings
.write_to_file(out_path.join("mylog_bindings.rs"))
.expect("Couldn't write bindings!");
//Compile and link a static library named `myLog`:
cc::Build::new()
.file("myLog.c")
.compile("myLog");
}
And do not forget to add the cc crate to your build-dependencies.

Why does use libc stop cargo from linking my program correctly?

I have some C code which I compiled to a .so file which i want to be called from a Rust program.
// hello.c
void greet() {
printf("Hello, world");
}
so I compiled it to a shared object file and added it to my build.rs and it worked fine
// main.rs
#[link(name = "hello")]
extern "C" {
fn greet();
}
fn main() {
unsafe {
greet();
}
}
The problem is the I have a second function in my C code which accepts a char* as a parameter so I tried to use libc::c_char to communicate between C and Rust but whenever my program doesn't compile when I import libc.
// main.rs
#[link(name = "hello")]
use libc::c_char;
extern "C" {
greet();
}
And I already tried to compile just with import libc (because I thought that might have been the problem) but it works perfectly so it seems like the program only doesn't compile when I am using my C shared object and importing the libc crate.
This is the error message
error: linking with `cc` failed: exit code: 1
= note: "cc"
= note: Undefined symbols for architecture x86_64:
"_greet", referenced from:
project::main::h501a37fa09c5db9f in project.2q2eogqn7p5k3u7s.rcgu.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
The #[link] attribute must be just before the extern block. By inserting a use between the #[link] attribute and the extern block, the #[link] attribute becomes attached to the use and has no effect. (There really should be a warning for this...)
Works just fine for me, are you sure you compiled a static library that Rust linker can use regardless of what else gets linked into the final executable?
I can only guess that this is whats wrong as you haven't provided how exactly you setup your project, and I'd recommend letting cc crate handle it for you, and if you really need something it doesn't have, contribute to it, instead of manually compiling C code and trying to link it in.
Example
build.rs
fn main() {
cc::Build::new()
.file("src/hello.c")
.compile("hello");
}
src/hello.c
#include <stdio.h>
void greet() {
printf("Hello, world\n");
}
src/main.rs
use libc::c_char;
#[link(name = "hello")]
extern "C" {
fn greet();
}
fn main() {
unsafe {
greet();
}
}
cli
$ cargo run
Compiling link v0.1.0 (~/Desktop/link)
warning: unused import: `libc::c_char`
--> src/main.rs:4:5
|
4 | use libc::c_char;
| ^^^^^^^^^^^^
|
= note: `#[warn(unused_imports)]` on by default
warning: 1 warning emitted
Finished dev [unoptimized + debuginfo] target(s) in 0.50s
Running `~/.cargo/target/debug/link`
Hello, world

How to call external function in an external file. C

I have one base library written in C. And other external extensions/definitions files. With a simple search tool the base library (when is used) can search in a directory for extensions libraries. Without telling the compiler which files it are. Then the C program can call a function in that external file.
So i want to run a function in a external file. I can choose which file I want to run the function from at running time. (Ex, I enter that the file is located at ./external_file at running time) (NOT COMPILE TIME!). Is this possible?
I need to read a return from the function, I need to wait until the function is finished, I need to call the function with arguments and it must be possible that the program at run time can choose the file and function.
Is this possible? Yes, program files does contains function names (with names). Only I need to know HOW do I call that function in a external file at run time.
Use dynamic libraries - most OS's provide support for loading a library given a name and getting the address of the function to call.
In windows see LoadLibrary and ProcAddress. In linux see dlopen and dlsym.
You can use the dlopen function to load a shared library (i.e. a .so file) at runtime, then use dlsym to get the functions to call.
For example, suppose you had the following library file:
mylib.c:
#include <stdio.h>
void f1(int a)
{
printf("in f1, a=%d\n", a);
}
int f2(char *s)
{
printf("in f2, s=%s\n", s);
return *s;
}
Then you compile it to a shared library:
gcc -g -Wall -Wextra -fPIC -c mylib.c
gcc -g -Wall -Wextra -fPIC -o libmylib.so mylib.o
You can call these functions as follows:
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
int main()
{
// get a handle to the shared library
void *handle = dlopen("./libmylib.so", RTLD_LAZY);
if (!handle) {
printf("dlopen failed: %s\n", dlerror());
exit(1);
}
// get a pointer to the function to call
void (*myf1)(int) = dlsym(handle, "f1");
if (!myf1) {
printf("dlsym 1 failed: %s\n", dlerror());
exit(1);
}
myf1(4);
// get a pointer to another function to call
int (*myf2)(char *) = dlsym(handle, "f2");
if (!myf2) {
printf("dlsym 2 failed: %s\n", dlerror());
exit(1);
}
printf("rval=%d\n", myf2("hello"));
dlclose(handle);
return 0;
}
Note that you need to know at compile time what the function signatures look like so you can call them correctly.

Rust and C linking problems with minimal program and no_std

I'm trying to build a minimal program in C that calls Rust functions, preferably compiled with #![no_std], in Windows, using GCC 6.1.0 and rustc 1.11.0-nightly (bb4a79b08 2016-06-15) x86_64-pc-windows-gnu. Here's what I tried first:
main.c
#include <stdio.h>
int sum(int, int);
int main()
{
printf("Sum is %d.\n", sum(2, 3));
return 0;
}
sum.rs
#![no_std]
#![feature(libc)]
extern crate libc;
#[no_mangle]
pub extern "C" fn sum(x: libc::c_int, y: libc::c_int) -> libc::c_int
{
x + y
}
Then I tried running:
rustc --crate-type=staticlib --emit=obj sum.rs
But got:
error: language item required, but not found: `panic_fmt`
error: language item required, but not found: `eh_personality`
error: language item required, but not found: `eh_unwind_resume`
error: aborting due to 3 previous errors
OK, so some of those errors are related to panic unwinding. I found out about a Rust compiler setting to remove unwinding support, -C panic=abort. Using that, the errors about eh_personality and eh_unwind_resume disappeared, but Rust still required the panic_fmt function. So I found its signature at the Rust docs, then I added that to the file:
sum.rs
#![no_std]
#![feature(lang_items, libc)]
extern crate libc;
#[lang = "panic_fmt"]
pub fn panic_fmt(_fmt: core::fmt::Arguments, _file_line: &(&'static str, u32)) -> !
{ loop { } }
#[no_mangle]
pub extern "C" fn sum(x: libc::c_int, y: libc::c_int) -> libc::c_int
{
x + y
}
Then, I tried building the whole program again:
rustc --crate-type=staticlib --emit=obj -C panic=abort sum.rs
gcc -c main.c
gcc sum.o main.o -o program.exe
But got:
sum.o:(.text+0x3e): undefined reference to `core::panicking::panic::h907815f47e914305'
collect2.exe: error: ld returned 1 exit status
The panic function reference is probably from a overflow check in the addition at sum(). That's all fine and desirable. According to this page, I need to define my own panic function to work with libcore. But I can't find instructions on how to do so: the function for which I am supposed to provide a definition is called panic_impl in the docs, however the linker is complaining about panic::h907815f47e914305, whatever that's supposed to be.
Using objdump, I was able to find the missing function's name, and hacked that into C:
main.c
#include <stdio.h>
#include <stdlib.h>
int sum(int, int);
void _ZN4core9panicking5panic17h907815f47e914305E()
{
printf("Panic!\n");
abort();
}
int main()
{
printf("Sum is %d.\n", sum(2, 3));
return 0;
}
Now, the whole program compiles and links successfully, and even works correctly.
If I then try using arrays in Rust, another kind of panic function (for bounds checks) is generated, so I need to provide a definition for that too. Whenever I try something more complex in Rust, new errors arise. And, by the way, panic_fmt seems to never be called, even when a panic does happen.
Anyways, this all seems very unreliable, and contradicts every information I could find via Google on the matter. There's this, but I tried to follow the instructions to no avail.
It seems such a simple and fundamental thing, but I can't get it to work the right way. Perhaps it's a Rust nightly bug? But I need libc and lang_items. How can I generate a Rust object file/static library without unwinding or panic support? It should probably just execute an illegal processor instruction when it wants to panic, or call a panic function I can safely define in C.
You shouldn't use --emit=obj; just rustc --crate-type=staticlib -C panic=abort sum.rs should do the right thing. (This fixes the _ZN4core9panicking5panic17h907815f47e914305E link error.)
To fix another link error, you need to write panic_fmt correctly (note the use of extern):
#[lang="panic_fmt"]
extern fn panic_fmt(_: ::core::fmt::Arguments, _: &'static str, _: u32) -> ! {
loop {}
}
With those changes, everything appears to work the way it's supposed to.
You need panic_fmt so you can decide what to do when a panic happens: if you use #![no_std], rustc assumes there is no standard library/libc/kernel, so it can't just call abort() or expect an illegal instruction to do anything useful. It's something which should be exposed in stable Rust somehow, but I don't know if anyone is working on stabilizing it.
You don't need to use #![feature(libc)] to get libc; you should use the version posted on crates.io instead (or you can declare the functions you need by hand).
So, the solution, from the accepted answer, was:
main.c
#include <stdio.h>
#include <stdlib.h>
int sum(int, int);
void panic(const char* filename_unterminated, int filename_size, int line_num)
{
printf("Panic! At line %d, file ", line_num);
for (int i = 0; i < filename_size; i++)
printf("%c", filename_unterminated[i]);
abort();
}
int main()
{
// Sum as u8 will overflow to test panicking.
printf("Sum is %d.\n", sum(0xff, 3));
return 0;
}
sum.rs
#![no_std]
#![feature(lang_items, libc)]
extern crate libc;
extern "C"
{
fn panic(
filename_unterminated: *const libc::c_char,
filename_size: libc::c_int,
line_num: libc::c_int) -> !;
}
#[lang="panic_fmt"]
extern fn panic_fmt(_: ::core::fmt::Arguments, filename: &'static str, line_num: u32) -> !
{
unsafe { panic(filename.as_ptr() as _, filename.len() as _, line_num as _); }
}
#[no_mangle]
pub extern "C" fn sum(x: libc::c_int, y: libc::c_int) -> libc::c_int
{
// Convert to u8 to test overflow panicking.
((x as u8) + (y as u8)) as _
}
And compiling with:
rustc --crate-type=staticlib -C panic=abort sum.rs
gcc -c main.c
gcc main.o -L . -l sum -o program.exe
Now everything works, and I have a panic handler in C that shows where the error occurred!

SystemVerilog: How to connect C function using DPI call in VCS simulator?

I have the following files:
C file with functions:
// funcs.c
#include <stdio.h>
void something() {
printf("something\n");
sayHello();
}
System verilog file:
// hello_world.v
module kuku;
export "DPI-C" function sayHello;
import "DPI-C" function void something();
initial something();
function int sayHello ();
$display("hello world");
sayHello = 1;
endfunction
endmodule
How can I compile it and make this work so when I call something() from SV, it will call the C function, and when I call sayHello() from C, it will call the SV function?
Answering myself:
When SV code is compiled using VCS, it is first translated into C code.
When exporting a function out of SV, it generates a C header file vc_hdrs.h that should be included by the C file.
So a change I made in the C file is to add the line:
#include "vc_hdrs.h"
Then, I just added the C functions file to the VCS compilation command:
> vcs -sverilog hello_world.v funcs.c
It works!
The output I get is:
something
hello world
.
A solution that works with all simulator that follow IEEE Std 1800-2012 is to have #include "svdpi.h" and prefix the extern keyword in front of all methods being exported to C. funcs.c should look like:
#include <stdio.h>
#include "svdpi.h"
extern int sayHello();
void something() {
printf("something\n");
sayHello();
}
Examples from IEEE Std 1800-2012
§ H.10.2 Example 2—Simple packed array application
§ H.10.3 Example 3—Application with complex mix of types
I see you've named SystemVerilog file as .v extension. Not sure if that works or not. But lets say if its hello_world.sv
Your command line should look like this (for Questa Simulator),
qverilog hello_world.sv funcs.c
"qverilog " is to compile and run SystemVerilog files.
That's all. No need to add extra header files
Hi I have provided a nice example under this post
https://stackoverflow.com/a/46441794/5842403
Synopsys VCS
1) You compile the C code using flags and introducing the defines you want to add.
In our case our C code need the define PYTHON_PATH
#GCC in two steps for shared object
gcc -g -D 'PYTHON_PATH="'$PYTHON_DIR'"' -fPIC -Wall -I${VCS_HOME}/include -I/usr/include/python2.6/ -lpython2.6 -c ${PROJECTDIR}/verification/PVE/keycontrol/tb/keycontrol_C_code_wrapper.c
gcc -fPIC -shared -o keycontrol_C_code_wrapper.so keycontrol_C_code_wrapper.o
2) You do the VCS elaboration linking the python lybrary with -LDFLAGS '-lpython2.6'
vcs -timescale=1ps/1ps -ntb_opts uvm -lca -kdb -full64 keycontrol_tb_top -debug_access+all+reverse -LDFLAGS '-lpython2.6'
3) You run the created simulation file. You call simv including -sv_lib keycontrol_C_code_wrapper to import the C shared object.
#RUN C CODE
./simv -gui -ucli +DVE +UVM_NO_RELNOTES -l simv.log +UVM_TESTNAME=keycontrol_basic_test -do ../../verification/PVE/keycontrol/tools/keycontrol_ucli_init.synopsys -sv_lib keycontrol_C_code_wrapper

Resources