如何使用库导入编译 C 文件到 webassembly 文件(Emscripten)

Posted

技术标签:

【中文标题】如何使用库导入编译 C 文件到 webassembly 文件(Emscripten)【英文标题】:How to compile C file with Library imports to webassembly file (Emscripten) 【发布时间】:2020-02-25 09:15:37 【问题描述】:

我有一个需要解析 Json 数据的简单 C 程序。为此,我导入了 JSON-C 库。我的 C 代码是 -

#include"json.h"
#include <emscripten.h>

EMSCRIPTEN_KEEPALIVE
int addnumbers(int a, int b) 
    FILE *fp;
    char buffer[1024];
    struct json_object *parsed_json;
    struct json_object *name;
    struct json_object *age;
    struct json_object *friends;
    struct json_object *friend;
    size_t n_friends;

    size_t i;

    fp = fopen("test.json","r");
    fread(buffer, 1024, 1, fp);
    fclose(fp);

    parsed_json = json_tokener_parse(buffer); 

    json_object_object_get_ex(parsed_json, "name", &name);
    json_object_object_get_ex(parsed_json, "age", &age);
    json_object_object_get_ex(parsed_json, "friends", &friends);

    printf("Name: %s\n", json_object_get_string(name));
    printf("Age: %d\n", json_object_get_int(age));

    n_friends = json_object_array_length(friends);


    for(i=0;i<n_friends;i++) 
        friend = json_object_array_get_idx(friends, i);
        // printf("%lu. %s\n",i+1,json_object_get_string(friend));
    
    return n_friends;

我遵循的过程:- 使用命令将库(特别是 json.h 文件)编译为位代码-

emcc json.h -o json.bc

然后使用 - 编译我的 C 程序

emcc json.c -o j_plumbing.bc -s EXTRA_EXPORTED_RUNTIME_METHODS=['ccall','cwrap'] -s ENVIRONMENT='web,worker' -s EXPORT_ES6=1 -s MODULARIZE=1 -s USE_ES6_IMPORT_META=0

然后我一起编译这两个文件以使用此命令获取 wasm 文件:-

emcc json.bc j_plumbing.bc -o js_plumbing.js -s EXTRA_EXPORTED_RUNTIME_METHODS=['ccall','cwrap'] -g4 -s LINKABLE=1 -s EXPORT_ALL=1 -s ENVIRONMENT='web,worker' -s EXPORT_ES6=1 -s MODULARIZE=1 -s USE_ES6_IMPORT_META=0 

这就是我从 Vue 文件中调用它的方式

public draw_outline() 
        Module().then(myModule => 
            console.log(myModule)
            const result = myModule.ccall('addnumbers',
                'number',
                ['number', 'number'],
                [4, 6]);
            console.log("Value from wasm file", result);
        );
    
but this is the error I'm getting-

002210ee:1 Uncaught (in promise) RuntimeError: function signature mismatch
    at fclose (wasm-function[524]:0x1a777)
    at addnumbers (wasm-function[148]:0x6a45)
    at Module._addnumbers (webpack-internal:///./src/components/js_plumbing.js:1098:4989)
    at Object.ccall (webpack-internal:///./src/components/js_plumbing.js:199:628)
    at eval (webpack-internal:///./node_modules/cache-loader/dist/cjs.js?!./node_modules/babel-loader/lib/index.js!./node_modules/ts-loader/index.js?!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/components/Extraction.vue?vue&type=script&lang=ts&:128:31)
    at Object.Module.onRuntimeInitialized (webpack-internal:///./src/components/js_plumbing.js:1109:95)
    at doRun (webpack-internal:///./src/components/js_plumbing.js:1117:140)
    at run (webpack-internal:///./src/components/js_plumbing.js:1117:436)
    at runCaller (webpack-internal:///./src/components/js_plumbing.js:1113:15)
    at removeRunDependency (webpack-internal:///./src/components/js_plumbing.js:373:843)

谁能指出我在这里做错了什么?任何帮助表示赞赏

【问题讨论】:

【参考方案1】:

说明

如果您仔细阅读错误和调用堆栈,您会注意到问题源于fclose() 函数。使用emscripten 生成的WebAssembly 模块有其virtual file system,它不了解您机器上的本地文件系统。因此,任何对本地文件的访问都会失败,就像fp = fopen("test.json","r"); 一样,它会返回NULLfp指针的这个NULL-value就是fclose(fp)出错的原因。

由于我无法使用您的代码(抱歉),我在稍微不同的设置中复制了错误,但在快速解决方案之后!

快速解决方案

使用例如将 WebAssembly/emscripten 的虚拟文件系统映射到本地文件系统NODEFS。您可以在我的另一个答案https://***.com/a/60510997/1319478 中找到有关此解决方案的更多信息。

#include <stdio.h>
#include <emscripten.h>
#include <emscripten/bind.h>

void test_fun()

   FILE *fp;
   EM_ASM(
       FS.mkdir('/temp');
       FS.mount(NODEFS, root : '.', '/temp'););
   fp = fopen("temp/test.json", "r");
   fclose(fp);


EMSCRIPTEN_BINDINGS(Module)

   emscripten::function("test_fun", &test_fun);

最简单的错误复现

此示例代码尝试关闭具有NULL 值指针的文件。

#include <stdio.h>
#include <emscripten.h>
#include <emscripten/bind.h>

void test_fun()

   fclose(NULL);


EMSCRIPTEN_BINDINGS(Module)

   emscripten::function("test_fun", &test_fun);

如果你用额外的调试信息编译这个例子-g:

emcc example.cpp -o example.js --bind -s WASM_ASYNC_COMPILATION=0 -g

然后尝试执行一个测试脚本node test.js,其中test.js如下:

var Module = require('./example.js');

Module.test_fun();

那么,你得到的是同样的错误:

RuntimeError: function signature mismatch
    at fclose (wasm-function[32]:127)
    at test_fun() (wasm-function[17]:9)
    ...

【讨论】:

对迟到的回复表示歉意,我还无法对此进行测试,但我确信这是解决方案。我感谢您的帮助。谢谢。

以上是关于如何使用库导入编译 C 文件到 webassembly 文件(Emscripten)的主要内容,如果未能解决你的问题,请参考以下文章

C语言中如何将自己常用的函数封装到编译器的库函数中具体应该怎么做呢?

iOS静态库引用

如何链接到 C 中的静态库?

如何编译C/Fortran动态/静态链接库

Ufft示例编译

为啥C程序多文件编译,没有导入自己的头文件也能正常编译通过?