Isolate v8 symbols by lloeki · Pull Request #179 · rubyjs/mini_racer (original) (raw)

Hello!

We identified an issue happening when two extensions are built with different V8 versions, then loaded at the same time.

In an ideal world, all extensions would be built with -fvisibility=hidden, only exposing relevant symbols and avoiding clases, unfortunately that is not the case.

Indeed the libv8 gem does not build its static library with -fvisibility=hidden. The result is that when this static library is linked into a Ruby extension (IOW a dynamic library), V8 symbols are global, irrespective of passing -fvisibility=hidden when building the extension.

Additionally, Ruby doesn't really make an effort to load its extensions in isolated ways, resulting in the default of globally available symbols.

As a result, the second extension being loaded will resolve some of its V8 symbols to the first ones that were loaded.

To combat this, within Sqreen's fork of MiniRacer, we created a small wrapper that takes in charge loading the extension more privately by:

Unfortunately, the latter is not always available, resulting in MiniRacer's V8 symbols being picked up by other libraries, even when they use RTLD_LOCAL (which only controls things one way). In that case we are left with no option.

This PR attempts to make MiniRacer a good citizen in two ways:

Considered alternatives

Excerpt of mini_racer_extension.bundle symbol table (T means global, t means local`):

$ nm -C lib/mini_racer_extension.bundle | grep ' T' | head
0000000000003ce0 T _Init_mini_racer_extension
00000000001544b0 T _v8_internal_Get_Object(void*)
0000000000154870 T _v8_internal_Node_Print(void*)
0000000000154550 T _v8_internal_Print_Code(void*)
00000000001544f0 T _v8_internal_Print_Object(void*)
00000000001547c0 T _v8_internal_Print_StackTrace()
0000000000154800 T _v8_internal_Print_TransitionTree(void*)
0000000000154730 T _v8_internal_Print_LayoutDescriptor(void*)
000000000000f260 T V8_Fatal(char const*, ...)
000000000000f3b0 T V8_Dcheck(char const*, int, char const*)

Excerpt of a stack trace where code jumps from one lib to the other within libv8:

-- C level backtrace information -------------------------------------------
/Users/lloeki/.rubies/ruby-2.7.1/bin/ruby(rb_vm_bugreport+0x96) [0x106ebf616]
/Users/lloeki/.rubies/ruby-2.7.1/bin/ruby(rb_bug_for_fatal_signal+0x1d0) [0x106cf9270]
/Users/lloeki/.rubies/ruby-2.7.1/bin/ruby(sigsegv+0x5b) [0x106e22c4b]
/usr/lib/system/libsystem_platform.dylib(_sigtramp+0x1d) [0x7fff7248c5fd]
/Users/lloeki/.gem/ruby/2.7.1/gems/mini_racer-0.3.1/lib/mini_racer_extension.bundle(_ZN2v88internal12_GLOBAL__N_123GetPageTableInitializerEv+0x25) [0x10bade405]
/Users/lloeki/.gem/ruby/2.7.1/gems/mini_racer-0.3.1/lib/mini_racer_extension.bundle(0x10badea39) [0x10badea39]
/Users/lloeki/.gem/ruby/2.7.1/gems/mini_racer-0.3.1/lib/mini_racer_extension.bundle(0x10b77b3b6) [0x10b77b3b6]
/Users/lloeki/.gem/ruby/2.7.1/gems/mini_racer-0.3.1/lib/mini_racer_extension.bundle(0x10b77bc5e) [0x10b77bc5e]
/Users/lloeki/Workspace/github.com/sqreen/mini_racer/lib/sq_mini_racer_extension.bundle(_ZN2v88internal9SemiSpace6CommitEv+0x7a) [0x10d81df5a]
/Users/lloeki/Workspace/github.com/sqreen/mini_racer/lib/sq_mini_racer_extension.bundle(0x10d81de76) [0x10d81de76]
/Users/lloeki/Workspace/github.com/sqreen/mini_racer/lib/sq_mini_racer_extension.bundle(0x10d7a35ef) [0x10d7a35ef]
/Users/lloeki/Workspace/github.com/sqreen/mini_racer/lib/sq_mini_racer_extension.bundle(0x10d74cf00) [0x10d74cf00]
/Users/lloeki/Workspace/github.com/sqreen/mini_racer/lib/sq_mini_racer_extension.bundle(0x10d74c9fd) [0x10d74c9fd]
/Users/lloeki/Workspace/github.com/sqreen/mini_racer/lib/sq_mini_racer_extension.bundle(0x10d5e78fb) [0x10d5e78fb]
/Users/lloeki/Workspace/github.com/sqreen/mini_racer/lib/sq_mini_racer_extension.bundle(0x10d5e79af) [0x10d5e79af]
/Users/lloeki/Workspace/github.com/sqreen/mini_racer/lib/sq_mini_racer_extension.bundle(_ZN11IsolateInfo4initEP12SnapshotInfo+0xc2) [0x10d3ba012]
/Users/lloeki/Workspace/github.com/sqreen/mini_racer/lib/sq_mini_racer_extension.bundle(_ZL22rb_context_init_unsafemmm+0x126) [0x10d3bc016]
/Users/lloeki/.rubies/ruby-2.7.1/bin/ruby(vm_call_cfunc+0x16c) [0x106eb0cdc]
/Users/lloeki/.rubies/ruby-2.7.1/bin/ruby(vm_exec_core+0x38b0) [0x106e97050]