如何在JavaScript(EnScript)中重写C++ MalOC/FLUE?
我重写了Module.\u malloc和Module.\u在Javascriptemscripten中释放,方法是包装原始函数并添加Console.log以显示内存地址、大小和分配的内存总量如何在JavaScript(EnScript)中重写C++ MalOC/FLUE?,c++,emscripten,C++,Emscripten,我重写了Module.\u malloc和Module.\u在Javascriptemscripten中释放,方法是包装原始函数并添加Console.log以显示内存地址、大小和分配的内存总量 我发现新函数只捕获了javascript调用到Mult..MaLoC和Mule.Field,并且它没有捕获到Maloc和免费的C++级别调用。我想知道为什么 根据Ofria先生的回答,Module.\u malloc和Module.\u free是c++的malloc和free的转换等价代码 我正在使用e
我发现新函数只捕获了javascript调用到Mult..MaLoC和Mule.Field,并且它没有捕获到Maloc和免费的C++级别调用。我想知道为什么
根据Ofria先生的回答,Module.\u malloc和Module.\u free是c++的malloc和free的转换等价代码 我正在使用emscripten 1.35.0 编辑:下面是我如何用javascript包装函数的var _defaultMalloc = Module._malloc;
var _defaultFree = Module._free;
var _totalMemoryUsed = 0;
var _mallocTracker = {};
Module._malloc = function(size) {
_totalMemoryUsed += size;
var ptr = _defaultMalloc(size)
_mallocTracker[ptr] = size;
console.log("MALLOC'd @" + ptr + " " + size + " bytes -- TOTAL USED " + _totalMemoryUsed + " bytes");
return ptr;
}
Module._free = function(ptr) {
var size = _mallocTracker[ptr];
_totalMemoryUsed -= size;
console.log("FREE'd @" + ptr + " " + size + " bytes -- TOTAL USED " + _totalMemoryUsed + " bytes");
return _defaultFree(ptr);
}
简短的回答:您试图包装MalC/FILE的方法不起作用,因为公开Emscripten的MALOC/FULL实现的模块对象不是由本地C++代码调用的入口点。然而,通过一点黑客技术,你可以找到追踪这些电话的方法
为什么覆盖无效 <>我想你的答案可能会更好地表达:C++ C++的MALLC和免费调用的仿真在模块中公开。 注意:在这个答案的剩余部分,我通常只讨论malloc,但基本上适用于malloc的所有内容也适用于free 关于Emscripten如何处理malloc的所有血淋淋的细节,我将留待以后讨论,但简单地说:使用标准设置,EnScript将C++程序编译为A.Out.js.< 此文件的一大块创建了一个asm对象。这包含所有转换的C++代码,例如,C++、McLo.
的主要库函数和JavaScript版本的JavaScript实现。 <> > ASM内转换的C++代码在ASM .</P>内直接引用内部库函数。 <> >引用C++函数和许多库函数,特别是主函数、MyOLC和无空函数作为ASM对象的属性公开。它们还作为模块对象的属性公开,并作为独立变量存在这样,原始C++代码只调用在ASM代码块中定义的MyOLLC的内部实现。Emscripten框架的其余部分以及任何其他JavaScript代码也可以通过任何公开的引用调用此函数:_malloc、Module._malloc或Module['''u malloc']和asm._malloc或asm[''u malloc']
因此,如果使用包装版本替换任何或所有_malloc、Module._malloc或asm._malloc,这将只影响从Emscripten框架的其余部分或其他JavaScript代码进行的调用。它不会影响转换的C++代码调用。 跟踪调用_malloc/_free的方法 1.官方途径 在我们进入一些低级黑客之前,我应该提到Emscripten内置了一个跟踪API,根据他们的帮助页面,它提供了一些有用的功能,可以更好地查看应用程序内部的情况,特别是内存使用情况 我没有尝试过使用它,但对于严肃的调试工作来说,这可能是最好的选择。但是,您需要设置一个单独的进程来接收来自被测应用程序的跟踪消息,因此在某些情况下,这可能会有些过分 如果你想继续这样做,官方文档和描述了一家公司是如何利用跟踪API的,我与他们没有任何关系:那一页刚刚出现在搜索结果中 2.破解它上面提到的问题是,转换后的C++调用所调用的是ASM对象内部的函数,因此不受外部层创建的任何包装器的影响。经过一些调查,我想出了两种克服这个问题的方法。由于两者都有点老套,纯粹主义者可能想把目光移开
首先,让我们从一小段代码开始,作为我们的测试平台,根据页面上的内容进行调整: 你好,c 现在,我们将创建以下JavaScript文件来包装malloc并释放: tracemaloc.js 在Emscripten框架中有两个对_malloc的调用,但我们感兴趣的调用——来自C代码的调用——还没有被跟踪 2a。一次性黑客 如果我们检查a.out.js文件,我们将发现以下代码段,这是转换为JavaScript的C代码的开始:function _main() {
var $0 = 0, $1 = 0, $2 = 0, $3 = 0, $4 = 0, $fred = 0, $vararg_buffer = 0, label = 0, sp = 0;
sp = STACKTOP;
STACKTOP = STACKTOP + 16|0; if ((STACKTOP|0) >= (STACK_MAX|0)) abort();
$vararg_buffer = sp;
$0 = 0;
$1 = (_malloc(1234321)|0);
问题是对_malloc的调用引用了内部函数,而不是我们重写的函数。为了解决这个问题,我们可以编辑a.out.js,在_main的顶部添加以下两行:
这将使用asm对象持有的公共版本的引用替换内部属性_malloc和_free,到目前为止,这些公共版本已被我们的包装版本替换。虽然这看起来有点像循环,但它可以工作,因为包装版本已经存储了对真实malloc函数的引用,所以
你仍然这么叫,而不是我们刚刚覆盖的引用
如果现在重新运行a.out.js文件而不重建:
C:\Program Files\Emscripten\Test>node a.out.js
Wrapping malloc/free
_malloc( 42 )
<--- 5251080
_malloc( 5 )
<--- 5251128
_malloc( 1234321 )
<--- 5251144
Hello, world!
_free( 5251144 )
<--- undefined
在我的副本中,第一个更改位于第680行,第二个更改位于第964行。第一个更改告诉框架从asm对象导出函数wrapAllocFree;第二个更改定义了将要导出的函数。可以看出,这只是执行了我们在第2a节中手动编辑的两行代码,以及一个完全可选的跟踪行,以显示激活已经发生
为了利用这一变化,我们还需要在traceMalloc.js中取消对新函数调用的注释,使其内容如下:
return result ;
}
// Hack 2b: invoke semi-permanent code added to emscripten.py
asm.wrapMallocFree(); }
}
}
现在,我们可以重新构建和运行代码,并查看跟踪的所有调用,而无需手动编辑a.out.js:
对象asm是使用定义的。本质上,整个块定义了一个立即执行的匿名函数。执行该函数的结果就是分配给对象asm的结果。此执行在遇到上述代码时发生。IIFE的要点是,该匿名函数中定义的变量/函数仅对该函数中的代码可见。外部世界看到的只是该函数返回的分配给asm的内容
我们感兴趣的是,我们看到了_main转换的C代码和_mallocemscripten的内存分配器实现的定义。由于JavaScript/IIFE的工作方式,当在_main中执行代码时,它对_malloc的调用将始终引用这个内部版本的_malloc
IIFE的返回值是一个具有多个属性的对象。这个对象的属性名恰好与匿名函数中的对象/函数名相同。虽然这可能会让人感到困惑,但不涉及任何歧义。分配给asm的返回对象有一个名为_malloc的属性。该属性的值设置为等于内部对象的值\u malloc函数的定义实质上创建了一个属性/对象,该属性/对象引用了作为函数体的代码块。此引用可以像所有其他引用一样进行操作
模块的定义
施工后不久,我们有以下代码块:
var _free = Module["_free"] = asm["_free"];
var _main = Module["_main"] = asm["_main"];
var _i64Add = Module["_i64Add"] = asm["_i64Add"];
var _memset = Module["_memset"] = asm["_memset"];
var runPostSets = Module["runPostSets"] = asm["runPostSets"];
var _malloc = Module["_malloc"] = asm["_malloc"];
对于新创建的asm对象的选定属性,这有两件事:a它在第二个对象模块中创建属性,该属性引用与asm属性相同的内容;b它创建一些全局变量,这些变量也引用这些属性。全局变量供Emscripten框架的其他部分使用;模块对象供可能添加到Emscripten生成的代码中的其他JavaScript代码使用
所有的路都通向马洛克
在这一点上,我们有以下几点:
在用于创建asm的匿名函数中定义了一段代码,它提供了Emscripten对C/C++的_malloc函数的实现/仿真。这段代码是真正的malloc。应该注意的是,该代码或多或少独立于引用它的任何对象/属性
IIFE有一个名为_malloc的内部对象,当前引用上述代码。原始C/C++代码对malloc的调用将使用此对象的值进行
对象asm有一个名为_malloc的属性,该属性当前也引用上述代码块
对象模块还有一个名为_malloc的属性,该属性当前引用上述代码块
有一个全局对象\u malloc。毫不奇怪,它还引用了上述代码块
此时,在用于构建asm的IIFE中使用_mallocglobal scope,Module._malloc或Module['''u malloc'],asm._malloc或_malloc都将在同一代码块中结束–malloc的真正实现
在函数上下文中执行以下代码片段时:
只有这样,IIFE内部对象才会改变。执行时,它是新值asm.\u malloc正在引用我们的包装函数。此时,对malloc引用的所有四个变体都指向我们的包装器函数。该函数仍然可以通过变量real_malloc访问malloc的实际实现,因此现在,每当代码的任何部分调用malloc时,该调用都会通过我们的包装函数,以便跟踪调用。您是如何重写和包装该函数的?请提供您在代码中的具体操作。@Bumsikim感谢您的回复。我添加了代码。@德格我已经更新了我的答案,对引擎盖下发生的事情进行了正确的解释,并用了几种方法来追踪C++调用到Maloc。
function _main() {
var $0 = 0, $1 = 0, $2 = 0, $3 = 0, $4 = 0, $fred = 0, $vararg_buffer = 0, label = 0, sp = 0;
sp = STACKTOP;
STACKTOP = STACKTOP + 16|0; if ((STACKTOP|0) >= (STACK_MAX|0)) abort();
$vararg_buffer = sp;
$0 = 0;
$1 = (_malloc(1234321)|0);
function _main() {
_malloc = asm._malloc;
_free = asm._free;
C:\Program Files\Emscripten\Test>node a.out.js
Wrapping malloc/free
_malloc( 42 )
<--- 5251080
_malloc( 5 )
<--- 5251128
_malloc( 1234321 )
<--- 5251144
Hello, world!
_free( 5251144 )
<--- undefined
C:\Program Files\Emscripten\emscripten\1.35.0>fc emscripten.py.original emscripten.py
Comparing files emscripten.py.original and EMSCRIPTEN.PY
***** emscripten.py.original
exports = []
for export in all_exported:
***** EMSCRIPTEN.PY
exports = []
all_exported.append('wrapMallocFree') <--- Add this line
for export in all_exported:
*****
***** emscripten.py.original
// EMSCRIPTEN_START_FUNCS
function stackAlloc(size) {
***** EMSCRIPTEN.PY
// EMSCRIPTEN_START_FUNCS
function wrapMallocFree() { <--- Add these lines
console.log( 'wrapMallocFree()' ) ; <--- Add these lines
_malloc = asm._malloc ; <--- Add these lines
_free = asm._free ; <--- Add these lines
} <--- Add these lines
function stackAlloc(size) {
*****
return result ;
}
// Hack 2b: invoke semi-permanent code added to emscripten.py
asm.wrapMallocFree(); }
}
}
C:\Program Files\Emscripten\Test>emcc --pre-js traceMalloc.js hello.c
C:\Program Files\Emscripten\Test>node a.out.js
Wrapping malloc/free
wrapMallocFree()
_malloc( 42 )
<--- 5251080
_malloc( 5 )
<--- 5251128
_malloc( 1234321 )
<--- 5251144
Hello, world!
_free( 5251144 )
<--- undefined
// EMSCRIPTEN_START_ASM
var asm = (function(global, env, buffer) {
...
function _main() {
...
$1 = (_malloc(1234321)|0);
...
}
...
function _malloc($bytes) {
...
return ($mem$0|0);
}
...
return { ... _malloc: _malloc, ... };
})
// EMSCRIPTEN_END_ASM
(Module.asmGlobalArg, Module.asmLibraryArg, buffer);
var _free = Module["_free"] = asm["_free"];
var _main = Module["_main"] = asm["_main"];
var _i64Add = Module["_i64Add"] = asm["_i64Add"];
var _memset = Module["_memset"] = asm["_memset"];
var runPostSets = Module["runPostSets"] = asm["runPostSets"];
var _malloc = Module["_malloc"] = asm["_malloc"];
var real_malloc = _malloc ;
Module['_malloc'] = asm['_malloc'] = _malloc = function( size ) {
console.log( '_malloc( ' + size + ' )' ) ;
var result = real_malloc.apply( null, arguments ) ;
console.log( '<--- ' + result ) ;
return result ;
}
_malloc = asm._malloc ;