使用 Global with Value 时的 V8 段错误
V8 Segfault when using Global with Value
我开始嵌入 v8,遇到了一些 'unexpected behavior'。当变量 value_
不是 Reset
时,以下代码会生成 Segmentation fault (core dumped)
(请参阅代码中的注释)。但是,这不适用于上下文 context_
。为什么? This answer 似乎有关联,但没有提供解释。
我的期望是 isolate->Dispose()
兼顾两者。
#include <stdlib.h>
#include "include/libplatform/libplatform.h"
#include "include/v8.h"
int main(int argc, char* argv[]) {
v8::V8::InitializeICUDefaultLocation(argv[0]);
v8::V8::InitializeExternalStartupData(argv[0]);
std::unique_ptr<v8::Platform> platform = v8::platform::NewDefaultPlatform();
v8::V8::InitializePlatform(platform.get());
v8::V8::Initialize();
{
// Initialize V8.
// Create a new Isolate and make it the current one.
v8::Isolate::CreateParams create_params;
create_params.array_buffer_allocator =
v8::ArrayBuffer::Allocator::NewDefaultAllocator();
v8::Isolate* isolate = v8::Isolate::New(create_params);
v8::Global<v8::Context> context_;
v8::Global<v8::String> value_;
{
// Global Context Setup
v8::Isolate::Scope isolate_scope(isolate);
v8::HandleScope handle_scope(isolate);
v8::Local<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate);
v8::Local<v8::Context> context = v8::Context::New(isolate, NULL, global);
context_.Reset(isolate, context);
// Global Value Setup
v8::Context::Scope context_scope(context);
v8::Local<v8::String> value = v8::String::NewFromUtf8(isolate, "segfault", v8::NewStringType::kNormal).ToLocalChecked();
value_.Reset(isolate, value);
}
// value_.Reset(); // <- Why is this line needed?
// context_.Reset(); // <- Why is this line NOT needed?
isolate->Dispose();
delete create_params.array_buffer_allocator;
}
v8::V8::Dispose();
v8::V8::ShutdownPlatform();
return 0;
}
构建设置:
按照官方Getting started with embedding V8运行示例中的说明进行操作。将代码保存到 sample/wasm.cc 并执行以下命令:
$ g++ -I. -O2 -Iinclude samples/segfault.cc -o segfault -lv8_monolith -Lout.gn/x64.release.sample/obj/ -pthread -std=c++17
$ ./segfault
v8::Global
有一个 destructor 会调用 Reset()
。
全局句柄保存在Isolate
,在Isolate::Dispose()
,the global handles will be freed之后。
因此,如果你不调用Global::Reset()
,而是Dispose
在Global
的析构之前调用Isolate
,Global
的析构函数会导致释放后访问,这是典型的未定义行为。
Reset()
会将内部指针设置为 nullptr
,随后的调用将检查这一事实并且不执行任何操作。这就是为什么您可以在 Dispose()
之前添加一个 Reset()
以避免 UB。
您的 Global<Context>
也是如此,它并没有证明自己,因为释放后访问并不总是触发段错误。
我开始嵌入 v8,遇到了一些 'unexpected behavior'。当变量 value_
不是 Reset
时,以下代码会生成 Segmentation fault (core dumped)
(请参阅代码中的注释)。但是,这不适用于上下文 context_
。为什么? This answer 似乎有关联,但没有提供解释。
我的期望是 isolate->Dispose()
兼顾两者。
#include <stdlib.h>
#include "include/libplatform/libplatform.h"
#include "include/v8.h"
int main(int argc, char* argv[]) {
v8::V8::InitializeICUDefaultLocation(argv[0]);
v8::V8::InitializeExternalStartupData(argv[0]);
std::unique_ptr<v8::Platform> platform = v8::platform::NewDefaultPlatform();
v8::V8::InitializePlatform(platform.get());
v8::V8::Initialize();
{
// Initialize V8.
// Create a new Isolate and make it the current one.
v8::Isolate::CreateParams create_params;
create_params.array_buffer_allocator =
v8::ArrayBuffer::Allocator::NewDefaultAllocator();
v8::Isolate* isolate = v8::Isolate::New(create_params);
v8::Global<v8::Context> context_;
v8::Global<v8::String> value_;
{
// Global Context Setup
v8::Isolate::Scope isolate_scope(isolate);
v8::HandleScope handle_scope(isolate);
v8::Local<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate);
v8::Local<v8::Context> context = v8::Context::New(isolate, NULL, global);
context_.Reset(isolate, context);
// Global Value Setup
v8::Context::Scope context_scope(context);
v8::Local<v8::String> value = v8::String::NewFromUtf8(isolate, "segfault", v8::NewStringType::kNormal).ToLocalChecked();
value_.Reset(isolate, value);
}
// value_.Reset(); // <- Why is this line needed?
// context_.Reset(); // <- Why is this line NOT needed?
isolate->Dispose();
delete create_params.array_buffer_allocator;
}
v8::V8::Dispose();
v8::V8::ShutdownPlatform();
return 0;
}
构建设置:
按照官方Getting started with embedding V8运行示例中的说明进行操作。将代码保存到 sample/wasm.cc 并执行以下命令:
$ g++ -I. -O2 -Iinclude samples/segfault.cc -o segfault -lv8_monolith -Lout.gn/x64.release.sample/obj/ -pthread -std=c++17
$ ./segfault
v8::Global
有一个 destructor 会调用Reset()
。全局句柄保存在
Isolate
,在Isolate::Dispose()
,the global handles will be freed之后。
因此,如果你不调用Global::Reset()
,而是Dispose
在Global
的析构之前调用Isolate
,Global
的析构函数会导致释放后访问,这是典型的未定义行为。
Reset()
会将内部指针设置为 nullptr
,随后的调用将检查这一事实并且不执行任何操作。这就是为什么您可以在 Dispose()
之前添加一个 Reset()
以避免 UB。
您的 Global<Context>
也是如此,它并没有证明自己,因为释放后访问并不总是触发段错误。