WASM未导出add函数求助:Emscripten+CMake构建问题排查
问题:自定义add函数未在WASM中导出
使用的构建工具
- cmake v3.26.3
- emsdk/emcc/em++ v3.1.43(Emscripten)
- gcc/g++ v14.0.3 (Apple clang)
构建完成后用wasm-objdump检查WASM文件,发现自定义的add函数未被导出,JS端调用时显示undefined。
项目文件内容
hpp/demo.hpp
#ifndef API_DEMO_H #define API_DEMO_H int add(int a, int b); #endif
cpp/demo.cpp
#include "demo.hpp" int add(int a, int b) { return a + b; }
cpp/CMakeLists.txt
include_directories(../hpp) file(GLOB_RECURSE SRC *.cpp) add_executable(api ${SRC}) set_target_properties(api PROPERTIES COMPILE_FLAGS "--no-entry") set_target_properties(api PROPERTIES LINK_FLAGS "--no-entry")
根目录CMakeLists.txt
cmake_minimum_required(VERSION 3.10) project(api) set(CMAKE_CXX_FLAGS_INIT "-std=c++17") set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS ON) set(CMAKE_CXX_FLAGS "-O3 -DNDEBUG") set(CMAKE_EXECUTABLE_SUFFIX ".wasm") add_subdirectory(cpp)
构建命令
emcmake cmake -B build -S . cmake --build build -j 8
wasm-objdump输出结果
api.wasm: file format wasm 0x1 Section Details: Type[4]: - type[0] () -> i32 - type[1] () -> nil - type[2] (i32) -> nil - type[3] (i32) -> i32 Function[5]: - func[0] sig=1 <_initialize> - func[1] sig=0 <__errno_location> - func[2] sig=0 <stackSave> - func[3] sig=2 <stackRestore> - func[4] sig=3 <stackAlloc> Table[1]: - table[0] type=funcref initial=2 max=2 Memory[1]: - memory[0] pages: initial=256 max=256 Global[1]: - global[0] i32 mutable=1 - init i32=66576 Export[7]: - memory[0] -> "memory" - func[0] <_initialize> -> "_initialize" - table[0] -> "__indirect_function_table" - func[1] <__errno_location> -> "__errno_location" - func[2] <stackSave> -> "stackSave" - func[3] <stackRestore> -> "stackRestore" - func[4] <stackAlloc> -> "stackAlloc" Elem[1]: - segment[0] flags=0 table=0 count=1 - init i32=1 - elem[1] = func[0] <_initialize> Code[5]: - func[0] size=3 <_initialize> - func[1] size=5 <__errno_location> - func[2] size=4 <stackSave> - func[3] size=6 <stackRestore> - func[4] size=16 <stackAlloc>
HTML/JavaScript验证代码
<html> <head> <title>Simple template</title> </head> <body> <script> const importObject = { env: { __linear_memory: new WebAssembly.Memory({ initial: 256 }), __stack_pointer: new WebAssembly.Global({value:'i32', mutable:true,}, 0), } }; WebAssembly.instantiateStreaming( fetch('build/cpp/api.wasm'), importObject ).then(result => { console.log(result); console.log(result.instance.exports); console.log(result.instance.exports.add); // undefined }); </script> </body> </html>
问题原因与解决建议
原因
- 编译器优化移除未引用函数:开启了
-O3优化级别,编译器会自动移除项目内无引用、也未明确标记保留的函数,add函数符合这个情况,因此被优化剔除。 - 未显式指定导出函数:Emscripten不会自动导出所有C/C++函数,必须主动告知编译器哪些函数需要暴露给WASM环境。
解决方法
方法1:代码中标记导出函数
在函数声明或定义前添加EMSCRIPTEN_KEEPALIVE宏,强制编译器保留并导出该函数:
修改hpp/demo.hpp:
#ifndef API_DEMO_H #define API_DEMO_H #include <emscripten.h> EMSCRIPTEN_KEEPALIVE int add(int a, int b); #endif
或者在函数定义处添加:
#include "demo.hpp" #include <emscripten.h> EMSCRIPTEN_KEEPALIVE int add(int a, int b) { return a + b; }
方法2:通过链接参数指定导出函数
在cpp/CMakeLists.txt的链接参数中添加-s EXPORTED_FUNCTIONS=['_add'](C++默认会对函数名做修饰,因此需要加下划线前缀):
修改LINK_FLAGS配置:
set_target_properties(api PROPERTIES LINK_FLAGS "--no-entry -s EXPORTED_FUNCTIONS=['_add']")
如果想避免C++名字修饰,可以用extern "C"包裹函数声明:
#ifndef API_DEMO_H #define API_DEMO_H extern "C" { int add(int a, int b); } #endif
此时链接参数可改为-s EXPORTED_FUNCTIONS=['add']。
方法3:临时关闭优化(仅用于测试)
将CMAKE_CXX_FLAGS中的-O3改为-O0,关闭优化后未被引用的函数也会被保留,但会导致WASM体积增大、运行效率降低,仅适合临时排查问题:
set(CMAKE_CXX_FLAGS "-O0 -DNDEBUG")
内容的提问来源于stack exchange,提问作者Jane Wayne
相关产品推荐
相关产品推荐

