Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Isolate v8 symbols #179

Merged
merged 6 commits into from
Oct 9, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ Gemfile.lock
/spec/reports/
/tmp/
lib/mini_racer_extension.so
lib/mini_racer_loader.so
*.bundle
19 changes: 19 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -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 change: 1 addition & 0 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ end
task :default => [:compile, :test]

gem = Gem::Specification.load( File.dirname(__FILE__) + '/mini_racer.gemspec' )
Rake::ExtensionTask.new( 'mini_racer_loader', gem )
Rake::ExtensionTask.new( 'mini_racer_extension', gem )


Expand Down
1 change: 1 addition & 0 deletions ext/mini_racer_extension/extconf.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
$CPPFLAGS += " -std=c++0x"
$CPPFLAGS += " -fpermissive"
$CPPFLAGS += " -DV8_COMPRESS_POINTERS"
$CPPFLAGS += " -fvisibility=hidden "

$CPPFLAGS += " -Wno-reserved-user-defined-literal" if IS_DARWIN

Expand Down
2 changes: 1 addition & 1 deletion ext/mini_racer_extension/mini_racer_extension.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1658,7 +1658,7 @@ static void set_ruby_exiting(VALUE value) {

extern "C" {

void Init_mini_racer_extension ( void )
__attribute__((visibility("default"))) void Init_mini_racer_extension ( void )
{
VALUE rb_mMiniRacer = rb_define_module("MiniRacer");
rb_cContext = rb_define_class_under(rb_mMiniRacer, "Context", rb_cObject);
Expand Down
8 changes: 8 additions & 0 deletions ext/mini_racer_loader/extconf.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
require 'mkmf'

extension_name = 'mini_racer_loader'
dir_config extension_name

$CPPFLAGS += " -fvisibility=hidden "

create_makefile extension_name
123 changes: 123 additions & 0 deletions ext/mini_racer_loader/mini_racer_loader.c
Original file line number Diff line number Diff line change
@@ -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);
}
12 changes: 11 additions & 1 deletion lib/mini_racer.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
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 "json"

Expand Down
2 changes: 1 addition & 1 deletion mini_racer.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ Gem::Specification.new do |spec|
spec.add_dependency 'libv8', MiniRacer::LIBV8_VERSION
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'
end