FEATURE: Isolate v8 symbols (#179)
Adds a custom loader for MiniRacer that isolates all symbols, this allows loading multiple versions of v8 into a single process.
This commit is contained in:
parent
86bdc6d942
commit
c79ff2301f
|
@ -0,0 +1,12 @@
|
||||||
|
/.bundle/
|
||||||
|
/.yardoc
|
||||||
|
Gemfile.lock
|
||||||
|
/_yardoc/
|
||||||
|
/coverage/
|
||||||
|
/doc/
|
||||||
|
/pkg/
|
||||||
|
/spec/reports/
|
||||||
|
/tmp/
|
||||||
|
lib/mini_racer_extension.so
|
||||||
|
lib/mini_racer_loader.so
|
||||||
|
*.bundle
|
|
@ -8,4 +8,5 @@ Gemfile.lock
|
||||||
/spec/reports/
|
/spec/reports/
|
||||||
/tmp/
|
/tmp/
|
||||||
lib/mini_racer_extension.so
|
lib/mini_racer_extension.so
|
||||||
|
lib/mini_racer_loader.so
|
||||||
*.bundle
|
*.bundle
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
ARG RUBY_VERSION=2.7
|
||||||
|
FROM ruby:${RUBY_VERSION}
|
||||||
|
|
||||||
|
# without this `COPY .git`, we get the following error:
|
||||||
|
# fatal: not a git repository (or any of the parent directories): .git
|
||||||
|
# but with it we need the full gem just to compile the extension because
|
||||||
|
# of gemspec's `git --ls-files`
|
||||||
|
# COPY .git /code/.git
|
||||||
|
COPY Gemfile mini_racer.gemspec /code/
|
||||||
|
COPY lib/mini_racer/version.rb /code/lib/mini_racer/version.rb
|
||||||
|
WORKDIR /code
|
||||||
|
RUN bundle install
|
||||||
|
|
||||||
|
COPY Rakefile /code/
|
||||||
|
COPY ext /code/ext/
|
||||||
|
RUN bundle exec rake compile
|
||||||
|
|
||||||
|
COPY . /code/
|
||||||
|
CMD bundle exec irb -rmini_racer
|
1
Rakefile
1
Rakefile
|
@ -11,6 +11,7 @@ end
|
||||||
task :default => [:compile, :test]
|
task :default => [:compile, :test]
|
||||||
|
|
||||||
gem = Gem::Specification.load( File.dirname(__FILE__) + '/mini_racer.gemspec' )
|
gem = Gem::Specification.load( File.dirname(__FILE__) + '/mini_racer.gemspec' )
|
||||||
|
Rake::ExtensionTask.new( 'mini_racer_loader', gem )
|
||||||
Rake::ExtensionTask.new( 'mini_racer_extension', gem )
|
Rake::ExtensionTask.new( 'mini_racer_extension', gem )
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ $CPPFLAGS += " -fPIC" unless $CPPFLAGS.split.include? "-rdynamic" or IS_DARWIN
|
||||||
$CPPFLAGS += " -std=c++0x"
|
$CPPFLAGS += " -std=c++0x"
|
||||||
$CPPFLAGS += " -fpermissive"
|
$CPPFLAGS += " -fpermissive"
|
||||||
$CPPFLAGS += " -DV8_COMPRESS_POINTERS"
|
$CPPFLAGS += " -DV8_COMPRESS_POINTERS"
|
||||||
|
$CPPFLAGS += " -fvisibility=hidden "
|
||||||
|
|
||||||
$CPPFLAGS += " -Wno-reserved-user-defined-literal" if IS_DARWIN
|
$CPPFLAGS += " -Wno-reserved-user-defined-literal" if IS_DARWIN
|
||||||
|
|
||||||
|
|
|
@ -1658,7 +1658,7 @@ static void set_ruby_exiting(VALUE value) {
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
||||||
void Init_mini_racer_extension ( void )
|
__attribute__((visibility("default"))) void Init_mini_racer_extension ( void )
|
||||||
{
|
{
|
||||||
VALUE rb_mMiniRacer = rb_define_module("MiniRacer");
|
VALUE rb_mMiniRacer = rb_define_module("MiniRacer");
|
||||||
rb_cContext = rb_define_class_under(rb_mMiniRacer, "Context", rb_cObject);
|
rb_cContext = rb_define_class_under(rb_mMiniRacer, "Context", rb_cObject);
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
require 'mkmf'
|
||||||
|
|
||||||
|
extension_name = 'mini_racer_loader'
|
||||||
|
dir_config extension_name
|
||||||
|
|
||||||
|
$CPPFLAGS += " -fvisibility=hidden "
|
||||||
|
|
||||||
|
create_makefile extension_name
|
|
@ -0,0 +1,123 @@
|
||||||
|
#include <ruby.h>
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
// Load a Ruby extension like Ruby does, only with flags that:
|
||||||
|
// a) hide symbols from other extensions (RTLD_LOCAL)
|
||||||
|
// b) bind symbols tightly (RTLD_DEEPBIND, when available)
|
||||||
|
|
||||||
|
void Init_mini_racer_loader(void);
|
||||||
|
|
||||||
|
static void *_dln_load(const char *file);
|
||||||
|
|
||||||
|
static VALUE _load_shared_lib(VALUE self, volatile VALUE fname)
|
||||||
|
{
|
||||||
|
(void) self;
|
||||||
|
|
||||||
|
// check that path is not tainted
|
||||||
|
SafeStringValue(fname);
|
||||||
|
|
||||||
|
FilePathValue(fname);
|
||||||
|
VALUE path = rb_str_encode_ospath(fname);
|
||||||
|
|
||||||
|
char *loc = StringValueCStr(path);
|
||||||
|
void *handle = _dln_load(loc);
|
||||||
|
|
||||||
|
return handle ? Qtrue : Qfalse;
|
||||||
|
}
|
||||||
|
|
||||||
|
// adapted from Ruby's dln.c
|
||||||
|
#define INIT_FUNC_PREFIX ((char[]) {'I', 'n', 'i', 't', '_'})
|
||||||
|
#define INIT_FUNCNAME(buf, file) do { \
|
||||||
|
const char *base = (file); \
|
||||||
|
const size_t flen = _init_funcname(&base); \
|
||||||
|
const size_t plen = sizeof(INIT_FUNC_PREFIX); \
|
||||||
|
char *const tmp = ALLOCA_N(char, plen + flen + 1); \
|
||||||
|
memcpy(tmp, INIT_FUNC_PREFIX, plen); \
|
||||||
|
memcpy(tmp+plen, base, flen); \
|
||||||
|
tmp[plen+flen] = '\0'; \
|
||||||
|
*(buf) = tmp; \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
// adapted from Ruby's dln.c
|
||||||
|
static size_t _init_funcname(const char **file)
|
||||||
|
{
|
||||||
|
const char *p = *file,
|
||||||
|
*base,
|
||||||
|
*dot = NULL;
|
||||||
|
|
||||||
|
for (base = p; *p; p++) { /* Find position of last '/' */
|
||||||
|
if (*p == '.' && !dot) {
|
||||||
|
dot = p;
|
||||||
|
}
|
||||||
|
if (*p == '/') {
|
||||||
|
base = p + 1;
|
||||||
|
dot = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*file = base;
|
||||||
|
return (uintptr_t) ((dot ? dot : p) - base);
|
||||||
|
}
|
||||||
|
|
||||||
|
// adapted from Ruby's dln.c
|
||||||
|
static void *_dln_load(const char *file)
|
||||||
|
{
|
||||||
|
char *buf;
|
||||||
|
const char *error;
|
||||||
|
#define DLN_ERROR() (error = dlerror(), strcpy(ALLOCA_N(char, strlen(error) + 1), error))
|
||||||
|
|
||||||
|
void *handle;
|
||||||
|
void (*init_fct)(void);
|
||||||
|
|
||||||
|
INIT_FUNCNAME(&buf, file);
|
||||||
|
|
||||||
|
#ifndef RTLD_DEEPBIND
|
||||||
|
# define RTLD_DEEPBIND 0
|
||||||
|
#endif
|
||||||
|
/* Load file */
|
||||||
|
if ((handle = dlopen(file, RTLD_LAZY|RTLD_LOCAL|RTLD_DEEPBIND)) == NULL) {
|
||||||
|
DLN_ERROR();
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
#if defined(RUBY_EXPORT)
|
||||||
|
{
|
||||||
|
static const char incompatible[] = "incompatible library version";
|
||||||
|
void *ex = dlsym(handle, "ruby_xmalloc");
|
||||||
|
if (ex && ex != (void *) &ruby_xmalloc) {
|
||||||
|
|
||||||
|
# if defined __APPLE__
|
||||||
|
/* dlclose() segfaults */
|
||||||
|
rb_fatal("%s - %s", incompatible, file);
|
||||||
|
# else
|
||||||
|
dlclose(handle);
|
||||||
|
error = incompatible;
|
||||||
|
goto failed;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
# endif
|
||||||
|
|
||||||
|
init_fct = (void (*)(void)) dlsym(handle, buf);
|
||||||
|
if (init_fct == NULL) {
|
||||||
|
error = DLN_ERROR();
|
||||||
|
dlclose(handle);
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Call the init code */
|
||||||
|
(*init_fct)();
|
||||||
|
|
||||||
|
return handle;
|
||||||
|
|
||||||
|
failed:
|
||||||
|
rb_raise(rb_eLoadError, "%s", error);
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((visibility("default"))) void Init_mini_racer_loader()
|
||||||
|
{
|
||||||
|
VALUE mMiniRacer = rb_define_module("MiniRacer");
|
||||||
|
VALUE mLoader = rb_define_module_under(mMiniRacer, "Loader");
|
||||||
|
rb_define_singleton_method(mLoader, "load", _load_shared_lib, 1);
|
||||||
|
}
|
|
@ -1,5 +1,15 @@
|
||||||
require "mini_racer/version"
|
require "mini_racer/version"
|
||||||
require "mini_racer_extension"
|
require "mini_racer_loader"
|
||||||
|
require "pathname"
|
||||||
|
|
||||||
|
ext_filename = "mini_racer_extension.#{RbConfig::CONFIG['DLEXT']}"
|
||||||
|
ext_path = Gem.loaded_specs['mini_racer'].require_paths
|
||||||
|
.map { |p| (p = Pathname.new(p)).absolute? ? p : Pathname.new(__dir__).parent + p }
|
||||||
|
ext_found = ext_path.map { |p| p + ext_filename }.find { |p| p.file? }
|
||||||
|
|
||||||
|
raise LoadError, "Could not find #{ext_filename} in #{ext_path.map(&:to_s)}" unless ext_found
|
||||||
|
MiniRacer::Loader.load(ext_found.to_s)
|
||||||
|
|
||||||
require "thread"
|
require "thread"
|
||||||
require "json"
|
require "json"
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ Gem::Specification.new do |spec|
|
||||||
spec.add_dependency 'libv8', MiniRacer::LIBV8_VERSION
|
spec.add_dependency 'libv8', MiniRacer::LIBV8_VERSION
|
||||||
spec.require_paths = ["lib", "ext"]
|
spec.require_paths = ["lib", "ext"]
|
||||||
|
|
||||||
spec.extensions = ["ext/mini_racer_extension/extconf.rb"]
|
spec.extensions = ["ext/mini_racer_loader/extconf.rb", "ext/mini_racer_extension/extconf.rb"]
|
||||||
|
|
||||||
spec.required_ruby_version = '>= 2.3'
|
spec.required_ruby_version = '>= 2.3'
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue