I need to trigger the user's callback function at a specific time, but when the callback function is triggered, the object has been recycled and cannot be used. Is there any way to save the object without being recycled? After the callback is triggered, I want to manually reclaim the memory
LPVOID createCallbackFunc(void *lpMethod, Local<Function> *lpCB, Local<Context> *lpContext)
{
/*
t_cb - 55 - push rbp
014F0001- 48 8B EC - mov rbp,rsp
014F0004- 48 81 EC 90010000 - sub rsp,00000190
014F000B- 48 89 4C 24 08 - mov [rsp+08],rcx
014F0010- 48 89 54 24 10 - mov [rsp+10],rdx
014F0015- 4C 89 44 24 18 - mov [rsp+18],r8
014F001A- 4C 89 4C 24 20 - mov [rsp+20],r9
014F001F- 48 B9 0000000000000000 - mov rcx,0000000000000000
014F0029- 48 BA 0000000000000000 - mov rdx,0000000000000000
014F0033- 4C 8D 44 24 08 - lea r8,[rsp+08]
014F0038- 4C 8D 4D 10 - lea r9,[rbp+10]
014F003C- 48 B8 F0ACAB14FE7F0000 - mov rax,user32.MessageBoxA
014F0046- FF D0 - call rax
014F0048- 48 81 C4 90010000 - add rsp,00000190
014F004F- 48 8B E5 - mov rsp,rbp
014F0052- 5D - pop rbp
014F0053- C3 - ret
*/
string code_str = "55 48 8B EC 48 81 EC 90 01 00 00 48 89 4C 24 08 48 89 54 24 10 4C 89 44 24 18 4C 89 4C 24 20 48 B9 00 00 00 00 00 00 00 00 48 BA 00 00 00 00 00 00 00 00 4C 8D 44 24 08 4C 8D 4D 10 48 B8 F0 AC AB 14 FE 7F 00 00 FF D0 48 81 C4 90 01 00 00 48 8B E5 5D C3";
vector<BYTE> code_bytes = byteStr2Bytes(code_str);
memcpy_s(code_bytes.data() + 0x21, sizeof(uintptr_t), &lpCB, sizeof(uintptr_t));
memcpy_s(code_bytes.data() + 0x2B, sizeof(uintptr_t), &lpContext, sizeof(uintptr_t));
memcpy_s(code_bytes.data() + 0x3E, sizeof(uintptr_t), &lpMethod, sizeof(uintptr_t));
auto newmem = VirtualAlloc(0, code_bytes.size() + sizeof(uintptr_t), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
memcpy_s(newmem, code_bytes.size(), code_bytes.data(), code_bytes.size());
return newmem;
}
BOOL cb_test(Local<Function> &cb, Local<Context> &context, uintptr_t *a, uintptr_t *b)
{
// Will not print, and will give an error
if (cb->IsFunction())
{
printf("xxx\n");
}
return FALSE;
};
void callback(const FunctionCallbackInfo<Value> &args)
{
Isolate *isolate = args.GetIsolate();
auto context = isolate->GetCurrentContext();
auto cb = args[0].As<Function>();
LPVOID addr = createCallbackFunc(&cb_test, &cb, &context);
args.GetReturnValue().Set(Number::New(isolate, (uintptr_t)addr));
}
My current environment is windows 10 and nodejs 14.15.4
Your example is incomplete (e.g. what does createCallbackFunc
do?) so it's hard to be sure what's going on, but here are a few general points to consider:
Local<...>
is, as the name implies, meant to be used in a well-defined local scope. Its lifetime is tied to that of the surrounding HandleScope
. If createCallbackFunc
tries to stow it away somewhere, then it's expected that the Local
will become invalid soon (which doesn't say anything about the object it originally pointed to, and has nothing to do with the garbage collector). You can learn more about handles and their lifetimes at https://v8.dev/docs/embed.EDIT after updated question:
The source of createCallbackFunc
reveals two issues:
lpCB
is a pointer to a stack-allocated object, with memcpy_s(..., &lpCB, ..)
you're creating a copy of the address of this pointer. That can't work; firstly because lpCB
itself is very short-lived, and secondly because the stack-allocated cb
(in function callback
) that it refers to is similarly short-lived. So any pointer created this way will point to random garbage as soon as the callback
function returns. (This part of the problem is standard C++ and has nothing to do with V8 or Node or GC or Windows or any other specifics.)
As I guessed in my earlier answer, you're effectively trying to create a long-lived copy of a Local
. Use a Persistent
to accomplish that. Please read the documentation.
Summary: Don't use memcpy
on v8::Local
.