Stream: general

Topic: wasmtime on Android through the JNI


view this post on Zulip Jean (Sep 10 2025 at 06:36):

Hi! I'm trying to use the wasmtime c/c++ api on Android through the JNI. I downloaded the source files and imported them in my project. I adapted this example to read a file from the "Download" folder. But when I run the app, I get the following error :

: && /Users/.../Library/Android/sdk/ndk/27.0.12077973/toolchains/llvm/prebuilt/darwin-x86_64/bin/clang++ --target=aarch64-none-linux-android26 --sysroot=/Users/.../Library/Android/sdk/ndk/27.0.12077973/toolchains/llvm/prebuilt/darwin-x86_64/sysroot -fPIC -g -DANDROID -fdata-sections -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -D_FORTIFY_SOURCE=2 -Wformat -Werror=format-security -fno-limit-debug-info -static-libstdc++ -Wl,--build-id=sha1 -Wl,--no-rosegment -Wl,--no-undefined-version -Wl,--fatal-warnings -Wl,--no-undefined -Qunused-arguments -shared -Wl,-soname,libembeddedwasmtest.so -o /Users/.../Repos/EmbeddedWasmTest/app/build/intermediates/cxx/Debug/w6f68232/obj/arm64-v8a/libembeddedwasmtest.so CMakeFiles/embeddedwasmtest.dir/native-lib.cpp.o -landroid -llog /Users/.../Library/Android/sdk/ndk/27.0.12077973/toolchains/llvm/prebuilt/darwin-x86_64/sysroot/usr/lib/aarch64-linux-android/26/libandroid.so /Users/.../Library/Android/sdk/ndk/27.0.12077973/toolchains/llvm/prebuilt/darwin-x86_64/sysroot/usr/lib/aarch64-linux-android/26/liblog.so -latomic -lm && :

ld.lld: error: undefined symbol: wasm_engine_new

referenced by engine.hh:34 (/Users/.../Repos/EmbeddedWasmTest/app/src/main/cpp/wasmtime/include/wasmtime/engine.hh:34)
CMakeFiles/embeddedwasmtest.dir/native-lib.cpp.o:(wasmtime::Engine::Engine())

Any ideas how I can fix that?

view this post on Zulip Jean (Sep 10 2025 at 07:52):

Well turned out I needed to link the .a and .o library files with CMakeLists.txt. Now I get an error saying those files are not compatible : "[...].o is incompatible with armelf_linux_eabi". I guess I need to compile wasmtime with the correct target myself then?

view this post on Zulip Alex Crichton (Sep 10 2025 at 14:43):

Which downloadable artifact are you using from Wasmtime? To confirm are you using https://github.com/bytecodealliance/wasmtime/releases/download/v36.0.2/wasmtime-v36.0.2-aarch64-android-c-api.tar.xz?

Otherwise it's worth noting that Android for Wasmtime is a Tier 3 platform as no current maintainer has expertise on the platform and we don't have any other maintainer who has stepped up to maintain the target. That means that support is best-effort right now and while we're happy to help debug what we can and merge PRs/fixes it's worth setting expectations that it's unlikely everything works out of the box (or is thoroughly documented as other platforms are)

view this post on Zulip Jean (Sep 10 2025 at 18:19):

Yes, I'm using the aarch64-android c api. I do have experience with regular Android development but not so much with the native side. I'll see what I can do, maybe a PR some day!

view this post on Zulip Alex Crichton (Sep 10 2025 at 18:25):

ok yeah in that case I don't recognize that linker error message myself, but that does sound like the compilation flags in Rust are wrong and/or missing something? As to what though I'm not sure :(

view this post on Zulip Alex Crichton (Sep 10 2025 at 18:25):

and yeah we're more than happy to have PRs and/or suggestions here of how to improve things

view this post on Zulip Jean (Sep 10 2025 at 19:40):

But I noticed that the header files look duplicated inside the downloadable : wasmtime/include and wasmtime/min. I didn't find explanations on what's the difference between the two, do you know where I can learn more?

view this post on Zulip Alex Crichton (Sep 10 2025 at 19:45):

They're sort of like different sysroots, the "min" folder is built with almost all features disabled in the C API. The headers are exactly the same except for conf.h will will be different between the two. You probably want to just ignore the min folder

view this post on Zulip Jean (Sep 10 2025 at 21:35):

Some follow up on the topic : I get the app to compile but it still fails at runtime. I asked for help on SO https://stackoverflow.com/questions/79761309/link-third-party-c-library-through-the-jni

I'm trying to add the wasmtime c-api library to my app. But I'm getting the following error at runtime: java.lang.UnsatisfiedLinkError: dlopen failed: library "/Users/tufekoi/Repos/EmbeddedWa...

view this post on Zulip Jean (Sep 12 2025 at 07:55):

I think I figured it out! It was mainly due to a wrong configuration of my CMakeLists.txt file. But I had to modify the header files from wasmtime though. Android Studio complained it couldn't resolve the include statements to relative files using <path/to/header.h>. I had to replace them with "path/to/header.h". Several path were wrong too. Do you know how I can fix that in the wasmtime project? Then I can create a PR and maybe a documentation for android use.

view this post on Zulip Alex Crichton (Sep 12 2025 at 14:42):

In my experience this typically means a missing -I or compiler flag, but I'm also no C/C++ expert. I'm not sure why things work in the Wasmtime repo, for example, but don't work in your example. If you'd like feel free to send a PR and we can discuss further

view this post on Zulip Jean (Sep 13 2025 at 14:36):

I managed to complete the POC I wanted. I ended up creating an intermediary Rust library that compiles down to a C static library that I can include in my app. The Rust lib set up has only one function that takes a byte array from a wasm file, instanciate it based on a required wit, runs it and return a result.

#[unsafe(no_mangle)]
pub extern "C" fn execute_wasm(bytes: *const u8, wasm_len: usize) -> u32 {
    let wasm = unsafe { std::slice::from_raw_parts(bytes, wasm_len) };

    let engine = Engine::default();
    let component = Component::from_binary(&engine, wasm).expect("failed to compile component");

    let linker = Linker::new(&engine);
    let mut store = Store::new(&engine, ComponentState {});
    let bindings = Root::instantiate(&mut store, &component, &linker)
        .expect("failed to instantiate component");

    bindings
        .custom_wasi_component_one_add()
        .call_add(&mut store, 1, 2)
        .expect("failed to call `add` function")
}

On the android side, I only need to call that function from the JNI

extern "C" JNIEXPORT int JNICALL
Java_no_jean_embeddedwasmtest_MainActivity_executeWasm(
        JNIEnv *env,
        jobject /* this */,
        jbyteArray wasmBytesArray) {

    jsize length = env->GetArrayLength(wasmBytesArray);
    jbyte* bytes = env->GetByteArrayElements(wasmBytesArray, nullptr);
    int result = execute_wasm(reinterpret_cast<const uint8_t*>(bytes), length);
    env->ReleaseByteArrayElements(wasmBytesArray, bytes, JNI_ABORT);

    LOGD("All done!");
    return result;
}

And it works!

I would I've like to be able to use something similar to bindgen directly in c++, so I wouldn't need the rust library. I also assumed it was ok for the host to require components to implement a certain wit (does one say "implement a wit"?) Other thread regarding I started regarding that topic #general > Consuming wasi components with the wasmtime c api

view this post on Zulip Jean (Sep 19 2025 at 22:32):

I kept experimenting a bit : I compiled my custom rust library with ios (simulator) as a target and made a quick test app that open a wasi component file and run it from swift. The interop with C is actually even easier than on android.
I guess that mid-layer library is actually a good thing, now I can quite easily run wasi components from different platforms with a single codebase using the wasmtime library.

view this post on Zulip Victor Adossi (Sep 24 2025 at 00:26):

Quick question -- given the "no JITs on iOS" thing, are you planning on using pulley to run the component?

Happy to be wrong but I wonder if it's a thing that might pass in simulator but not on-device?

I heard that JIT compiled code is not allowed in iOS AppStore because placing executable code in heap is prohibited. It that right? Or just a rumor?
A lightweight WebAssembly runtime that is fast, secure, and standards-compliant - bytecodealliance/wasmtime

view this post on Zulip Jean (Sep 24 2025 at 19:51):

Well I didn't even know about that issue,, that SO thread refers to publication rules I think. Meaning it should run on-device the same way it runs on the simulator. But Apple seems to reject apps executing code that is not statically linked at the review time. That thread is from 2011 so maybe the rules are different now? I wouldn't be surprised if it's not, but it could be possible Apple agrees to let third-party wasm file be executed. One can hope!

view this post on Zulip Chris Fallin (Sep 24 2025 at 20:19):

The rules are still the same AFAIK.

view this post on Zulip Notification Bot (Sep 29 2025 at 10:39):

This topic was moved to #wasmtime > wasmtime on Android through the JNI by Till Schneidereit.


Last updated: Dec 06 2025 at 05:03 UTC