ブラウザでネイティブのC/C++コードを実行する


作業中WMIDumpper , ACPI WMIブロックを分析する簡単なツール、私は実装する方法を理解しなければならなかったbmfdec JavaScriptで.私の最初の考えはJavascriptに移植して、JSで1500コードのC行を書き換える時間と努力を入れました.しかし、その後、電球は私の頭、webassembly!簡単な検索でEmscripten まさに私が必要です.C/C++ネイティブコードをwebassemblyにコンパイルし、それをWeb上で実行できます.

ネイティブコードのビルド


クローンbmfdec そしてあとに続くdocumentation プロジェクトのコンパイル方法wasm . ビルbmfdec Emscriptenコンパイラを使用して簡単です.
emcc bmfdec/bmf2mof.c -o bmf2mof.js

ただ一つ問題があった.bmfdec stdinからバイナリファイルとして入力を行うために書かれました.私は修正しなければならなかったbmfdec.c そして、main() stdINからバイナリを読み込む代わりにバッファを取る関数parse_data() .
int parse_data(uint8_t *pin, ssize_t lin) {
  static char pout[0x40000];
  int lout;
  if (lin < 0) {
    fprintf(stderr, "Failed to read data: %s\n", strerror(errno));
    return 1;
  } else if (lin == sizeof(pin)) {
    fprintf(stderr, "Failed to read data: %s\n", strerror(EFBIG));
    return 1;
  }
  if (lin <= 16 || ((uint32_t*)pin)[0] != 0x424D4F46 || ((uint32_t*)pin)[1] != 0x01 || ((uint32_t*)pin)[2] != (uint32_t)lin-16 || ((uint32_t*)pin)[3] > sizeof(pout)) {
    fprintf(stderr, "Invalid input\n");
    return 1;
  }
  lout = ((uint32_t*)pin)[3];
  if (ds_dec((char *)pin+16, lin-16, pout, lout, 0) != lout) {
    fprintf(stderr, "Decompress failed\n");
    return 1;
  }
  return process_data(pout, lout);
}

ネイティブ関数のエクスポート


JavaScript内でC関数を呼び出すには、コンパイルする必要がありますbmf2mof いくつかの余分なフラグをモジュール化し、シンボルをJS出力ファイルにエクスポートします.
使用MODULARIZE コンパイラフラグを使用すると、約束を使用することができます生成されたJavascriptモジュラーになりますrequire() ノードで.EXPORT_NAME='bmf2mof' コンパイラフラグは、エクスポートされたモジュール名を変更しますbmf2mof() . WASM=1 WASM出力を指定します.そしてついに"EXPORTED_FUNCTIONS=['_parse_data']" 関数のエクスポートparse_data Cコードから.私たちもoptimize 出力JSコードを使用します.-O2 .
emcc bmfdec/bmf2mof.c -s "EXPORTED_FUNCTIONS=['_parse_data']" -s "MODULARIZE=1" -s "EXPORT_NAME='bmf2mof'" -s "WASM=1" -O2 -o bmf2mof.js

現在生成されるbmf2mof.js_parse_data 関数は、C関数にマップし、JavaScriptコードから呼び出すことができます.
const bmf2mof = require('bmf2mof.js')

const buf = new Uint8Array([0x46, 0x4F, 0x4D, 0x42, ..., 0x20, 0xEC, 0xFF, 0x0F])

bmf2mof().then(instance => {
    function arrayToPtr(array) {
        var ptr = instance._malloc(array.length)
        instance.HEAPU8.set(array, ptr)
        return ptr
    }

    instance._parse_data(arrayToPtr(buf), buf.length)
})

すべてが期待通りに動作し、上記のコードを実行しますnode [filename].js データをstdoutに出力します.しかし、ブラウザでこれを使いたいなら、出力はコンソールに行きます.そこで、出力をキャッチして欲しい場所にリダイレクトする方法を理解しなければなりませんWMIDumpper .
これは、生成されたJSファイルになると、文書を台無しに少し欠けていたので、私は何をしようと理解するために時間がかかった.生成されたJSは、C/C++からJSへのマッピングを処理する既定の関数を定義していることがわかりました.例えば、printErr バインドする関数console.warn stderrに出力するC/C++コードの意味console.warn JSで.参照Create the Module object より技術的な詳細については.
生成されたJSは、既定のモジュール関数をオーバーライドするオブジェクトを受け取ります.stdoutからHTMLのtextareaへの出力をリダイレクトするには、必要なのは自分自身を定義することですprint 関数.
const textarea = document.getElementById('textarea')

bmf2mof({
    print: function (text) {
        textarea.value += text + '\n'
    }
}).then(instance => ...)

もちろん、ブラウザでこのコードを使用する前に、生成されたJSスクリプトをHTMLに出力する必要があります.

リファレンス


あなたはwebassemblyフォークバージョンを見つけることができますbmfdec アットbmf2mof.wasm .
WMIDumpper 本当にウェブのクローンですwmidump and bmfdec だからiksaif and pali 彼らのものすごい仕事のために.