You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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>

问题原因与解决建议

原因

  1. 编译器优化移除未引用函数:开启了-O3优化级别,编译器会自动移除项目内无引用、也未明确标记保留的函数,add函数符合这个情况,因此被优化剔除。
  2. 未显式指定导出函数: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 14:11:58