Home > Software engineering >  How to link a C library in Rust WASI
How to link a C library in Rust WASI

Time:09-07

I want to use a C library in my rust-wasi program. but I am having trouble linking external libraries. My Current setup is this.

main.rs

#[link(name = "mylib")]
extern "C" {
    pub fn add_one(i: i32) -> i32;
}

pub fn main() {
    let res = unsafe { add_one(10) };
    println!("I   1: {}", res);
}

https://github.com/codeplea/tinyexpr mylib.cpp

#include "tinyexpr.h"

extern "C" int add_one(int i)
{
    te_interp("i   1", 0);
    return i   1;
}

build.rs

fn main() {
    cc::Build::new()
        .archiver("llvm-ar")
        .cpp_link_stdlib(None)
        .cpp(true)
        .flag("--sysroot=/opt/wasi-sysroot/")
        .file("mylib.cpp")
        .compile("libmylib.a");
}

leading to this error when I try to execute it with wasmtime.

cargo build --target wasm32-wasi --release 
wasmtime --dir=. --dir=/tmp target/wasm32-wasi/release/reverser.wasm
Error: failed to run main module `target/wasm32-wasi/release/so.wasm`

Caused by:
    0: failed to instantiate "target/wasm32-wasi/release/so.wasm"
    1: unknown import: `env::te_interp` has not been defined

I don't have any issues linking to the headers in the sys-root directory. Just with c headers in the same directory

CodePudding user response:

tinyexpr is not a header-only library, you also need to compile tinyexpr.c:

cc::Build::new()
    .archiver("llvm-ar")
    .flag(&sysroot)
    .file("tinyexpr.c")
    .compile("tinyexpr");

though you don't necessarily need to give it its own library, you could also compile tinyexpr.c and mylib.cpp into the same .a. From what I understand about C/C build processes, that should give you the same result.

If you wanted to be real pretty about it, you'd make a new tinyexpr-sys crate that contains just tinyexpr.c (plus a cbindgen-generated lib.rs).

Side note: For finding the sysroot, I'd go with something like

let sysroot = var("MYLIB_WASI_SYSROOT")
    .or(var("WASI_SYSROOT"))
    .ok()
    .or_else(|| Some(format!("{}/share/wasi-sysroot", var("WASI_SDK_PATH").ok()?)));
let sysroot = match sysroot {
    Some(sysroot) => format!("--sysroot={}", sysroot),
    None => {
        eprintln!(
            "Install wasi-sdk or wasi-libc and specify WASI_SYSROOT path in environment!"
        );
        exit(1);
    }
};

though you could also just expect people to set CFLAGS/CXXFLAGS.

Other side notes:

  • Related