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?
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?
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)
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!
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 :(
and yeah we're more than happy to have PRs and/or suggestions here of how to improve things
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?
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
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 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.
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
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
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.
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?
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!
The rules are still the same AFAIK.
This topic was moved to #wasmtime > wasmtime on Android through the JNI by Till Schneidereit.
Last updated: Dec 06 2025 at 05:03 UTC