使用Emscripten编译到WebAssembly时如何获取空闲内存起始地址?
我有一个C应用,其中包含如下接收字符串的函数:
void EMSCRIPTEN_KEEPALIVE modify_entity(char* target) { ... }
为与该函数交互,嵌入器需将符合C代码格式的字符串(此处为以空字符结尾的ASCII字符串)写入WebAssembly内存,并将内存地址指针传入函数。但我不清楚该将字符串写入内存的哪个位置?
查看生成的.wat文件,我发现Emscripten添加了多个数据段:
... (data $d10 (i32.const 1619) "\14") (data $d11 (i32.const 1631) "\17\00\00\00\00\17\00\00\00\00\09\14\00\00\00\00\00\14\00\00\14") (data $d12 (i32.const 1677) "\16") ...
据我了解,这些数据段都存在于同一个内存(即导出的单个(export "memory" (memory $memory)))中,因为目前WebAssembly实现仅支持单个内存。若写入地址1619会覆盖数据段内容,这显然不可取。
我的理解是否正确?若正确,嵌入器是否有办法获取有效的空闲内存地址?我可以手动查看.wat文件找到首个空闲内存,但想知道是否有对应的变量或函数可用?
我注意到一个全局变量(global $g0 (mut i32) (i32.const 67456))看起来有希望,但它在生成的WebAssembly代码中被广泛使用,不确定能否将其用作空闲内存指针。
或者我是否需要从WebAssembly中暴露malloc/free函数?我知道有不少实现可选,但希望嵌入器能自行处理,因为我仅需找到可安全使用的空闲内存。这是否可行?还是说这种做法不可行,因为会与C侧的内部内存分配产生冲突?
你的核心理解是正确的
所有Emscripten生成的数据段都存放在导出的单个Wasm内存中,直接写入数据段的地址会覆盖预存的代码/数据,绝对不能这么做。
关于空闲内存的获取方式
不要手动使用
$g0这类全局变量
你看到的$g0其实是Emscripten内部的__heap_base全局变量,它标记了堆内存的起始地址,但堆内存的管理完全由Emscripten的内存分配器(比如dlmalloc)掌控。直接修改或使用这个地址会破坏内部内存管理逻辑,导致内存泄漏、崩溃或数据损坏。推荐方案:暴露
malloc/free函数
这是最安全且标准的做法,具体步骤:
- 在C代码中添加
EMSCRIPTEN_KEEPALIVE标记来暴露malloc和free:#include <stdlib.h> void* EMSCRIPTEN_KEEPALIVE my_malloc(size_t size) { return malloc(size); } void EMSCRIPTEN_KEEPALIVE my_free(void *ptr) { free(ptr); } - 编译时确保Emscripten保留这些函数(默认会保留,除非你用了极其严格的死代码消除)。
- 嵌入器流程:
- 调用Wasm导出的
my_malloc函数,传入字符串长度+1(用于空终止符),得到可用的内存地址。 - 将字符串(包含空终止符)写入该地址对应的Wasm内存区域。
- 调用
modify_entity函数传入该地址。 - 使用完成后调用
my_free释放内存,避免内存泄漏。
- 调用Wasm导出的
嵌入器自行管理内存的可行性?
这种做法不可行,因为Emscripten的内存空间是由其内部分配器统一管理的。如果嵌入器自行选择"空闲"地址,很可能会和后续C代码的malloc/free操作冲突,导致内存重叠、数据损坏。只有通过Emscripten暴露的分配函数,才能保证内存操作的安全性和兼容性。替代方案:预分配固定内存块
如果你不想暴露malloc/free,可以在C代码中预先分配一块固定大小的内存,标记为EMSCRIPTEN_KEEPALIVE供嵌入器使用:
#define BUFFER_SIZE 1024 char EMSCRIPTEN_KEEPALIVE input_buffer[BUFFER_SIZE]; void modify_entity(char* target) { // 使用input_buffer或者直接处理传入的target }
编译后,你可以通过Emscripten的--export-table参数或查看.wat文件找到input_buffer的地址,嵌入器直接写入这个预分配的缓冲区即可。这种方式适合字符串长度固定且较小的场景,但灵活性不如malloc方案。
内容的提问来源于stack exchange,提问作者Michael Stum

