mudge / re2

Ruby bindings to RE2, a "fast, safe, thread-friendly alternative to backtracking regular expression engines like those used in PCRE, Perl, and Python".
http://mudge.name/re2/
BSD 3-Clause "New" or "Revised" License
130 stars 13 forks source link

Segfaults in re2_regexp_error after libre2 is upgraded #43

Closed stanhu closed 4 years ago

stanhu commented 4 years ago

I'm not sure if this is an actual issue with the C extension or whether this just means libre2 needs to be compiled with -fPIC for this not to happen (will test this out). But whenever libre2 is updated (e.g. from v1.1 to v1.2 recently in https://github.com/Homebrew/homebrew-core/blob/40c227a5885561519827edecffd959f6316163d8/Formula/re2.rb#L4), it seems that re2 seg faults if error is called. Running gem pristine re2 fixes the problem, but I'm wondering if this seg fault is something we can avoid. A simple script that reproduces the problem:

require 're2'

regexp = RE2::Regexp.new("{", log_errors: false)
puts regexp.error unless regexp.ok?
$ ruby re-test.rb
re-test.rb:4: [BUG] Segmentation fault at 0x0000000000000000
ruby 2.6.5p114 (2019-10-01 revision 67812) [x86_64-darwin19]

-- Crash Report log information --------------------------------------------
   See Crash Report log file under the one of following:
     * ~/Library/Logs/DiagnosticReports
     * /Library/Logs/DiagnosticReports
   for more details.
Don't forget to include the above Crash Report log file in bug reports.

-- Control frame information -----------------------------------------------
c:0003 p:---- s:0012 e:000011 CFUNC  :error
c:0002 p:0040 s:0008 E:000f78 EVAL   re-test.rb:4 [FINISH]
c:0001 p:0000 s:0003 E:002140 (none) [FINISH]

-- Ruby level backtrace information ----------------------------------------
re-test.rb:4:in `<main>'
re-test.rb:4:in `error'

-- Machine register context ------------------------------------------------
 rax: 0x00007fac19584c70 rbx: 0x00007fac098956e0 rcx: 0x0000000107cd12f0
 rdx: 0x000000000000000c rdi: 0x0000000000000000 rsi: 0x000000000000000c
 rbp: 0x00007ffee8386e10 rsp: 0x00007ffee8386e00  r8: 0x00007fac196d8d28
  r9: 0x00007fac20127f90 r10: 0x00007fac1a086600 r11: 0x00007fac19507028
 r12: 0x0000000000000000 r13: 0x00007fac098956e0 r14: 0x00007fac195862e0
 r15: 0x00007fac09897850 rip: 0x0000000107cd1317 rfl: 0x0000000000010206

-- C level backtrace information -------------------------------------------
/Users/stanhu/.rbenv/versions/2.6.5/lib/libruby.2.6.dylib(rb_vm_bugreport+0x82) [0x107abc572]
/Users/stanhu/.rbenv/versions/2.6.5/lib/libruby.2.6.dylib(rb_bug_context+0x1d6) [0x10790b746]
/Users/stanhu/.rbenv/versions/2.6.5/lib/libruby.2.6.dylib(sigsegv+0x51) [0x107a21261]
/usr/lib/system/libsystem_platform.dylib(_sigtramp+0x1d) [0x7fff6d5235fd]
/Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/re2-1.1.1/lib/re2.bundle(_ZL16re2_regexp_errorm+0x27) [0x107cd1317]
/Users/stanhu/.rbenv/versions/2.6.5/lib/libruby.2.6.dylib(vm_call_cfunc+0x156) [0x107aaede6]
/Users/stanhu/.rbenv/versions/2.6.5/lib/libruby.2.6.dylib(vm_exec_core+0x33da) [0x107a959ba]
/Users/stanhu/.rbenv/versions/2.6.5/lib/libruby.2.6.dylib(rb_vm_exec+0xac4) [0x107aa9834]
/Users/stanhu/.rbenv/versions/2.6.5/lib/libruby.2.6.dylib(ruby_exec_internal+0xe6) [0x1079167c6]
/Users/stanhu/.rbenv/versions/2.6.5/lib/libruby.2.6.dylib(ruby_run_node+0x49) [0x107916639]
/Users/stanhu/.rbenv/versions/2.6.5/bin/ruby(main+0x5d) [0x107878f0d]

-- Other runtime information -----------------------------------------------

* Loaded script: re-test.rb

* Loaded features:

    0 enumerator.so
    1 thread.rb
    2 rational.so
    3 complex.so
    4 /Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/2.6.0/x86_64-darwin19/enc/encdb.bundle
    5 /Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/2.6.0/x86_64-darwin19/enc/trans/transdb.bundle
    6 /Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/2.6.0/x86_64-darwin19/rbconfig.rb
    7 /Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/2.6.0/rubygems/compatibility.rb
    8 /Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/2.6.0/rubygems/defaults.rb
    9 /Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/2.6.0/rubygems/deprecate.rb
   10 /Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/2.6.0/rubygems/errors.rb
   11 /Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/2.6.0/rubygems/version.rb
   12 /Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/2.6.0/rubygems/requirement.rb
   13 /Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/2.6.0/rubygems/platform.rb
   14 /Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/2.6.0/rubygems/basic_specification.rb
   15 /Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/2.6.0/rubygems/stub_specification.rb
   16 /Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/2.6.0/delegate.rb
   17 /Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/2.6.0/uri/rfc2396_parser.rb
   18 /Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/2.6.0/uri/rfc3986_parser.rb
   19 /Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/2.6.0/uri/common.rb
   20 /Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/2.6.0/uri/generic.rb
   21 /Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/2.6.0/uri/file.rb
   22 /Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/2.6.0/uri/ftp.rb
   23 /Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/2.6.0/uri/http.rb
   24 /Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/2.6.0/uri/https.rb
   25 /Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/2.6.0/uri/ldap.rb
   26 /Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/2.6.0/uri/ldaps.rb
   27 /Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/2.6.0/uri/mailto.rb
   28 /Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/2.6.0/uri.rb
   29 /Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/2.6.0/rubygems/specification_policy.rb
   30 /Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/2.6.0/rubygems/util/list.rb
   31 /Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/2.6.0/x86_64-darwin19/stringio.bundle
   32 /Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/2.6.0/rubygems/specification.rb
   33 /Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/2.6.0/rubygems/exceptions.rb
   34 /Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/2.6.0/rubygems/util.rb
   35 /Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/2.6.0/rubygems/bundler_version_finder.rb
   36 /Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/2.6.0/rubygems/dependency.rb
   37 /Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/2.6.0/rubygems/core_ext/kernel_gem.rb
   38 /Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/2.6.0/monitor.rb
   39 /Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb
   40 /Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/2.6.0/rubygems/core_ext/kernel_warn.rb
   41 /Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/2.6.0/rubygems.rb
   42 /Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/2.6.0/rubygems/path_support.rb
   43 /Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/did_you_mean-1.3.0/lib/did_you_mean/version.rb
   44 /Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/did_you_mean-1.3.0/lib/did_you_mean/core_ext/name_error.rb
   45 /Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/did_you_mean-1.3.0/lib/did_you_mean/levenshtein.rb
   46 /Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/did_you_mean-1.3.0/lib/did_you_mean/jaro_winkler.rb
   47 /Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/did_you_mean-1.3.0/lib/did_you_mean/spell_checker.rb
   48 /Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/did_you_mean-1.3.0/lib/did_you_mean/spell_checkers/name_error_checkers/class_name_checker.rb
   49 /Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/did_you_mean-1.3.0/lib/did_you_mean/spell_checkers/name_error_checkers/variable_name_checker.rb
   50 /Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/did_you_mean-1.3.0/lib/did_you_mean/spell_checkers/name_error_checkers.rb
   51 /Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/did_you_mean-1.3.0/lib/did_you_mean/spell_checkers/method_name_checker.rb
   52 /Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/did_you_mean-1.3.0/lib/did_you_mean/spell_checkers/key_error_checker.rb
   53 /Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/did_you_mean-1.3.0/lib/did_you_mean/spell_checkers/null_checker.rb
   54 /Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/did_you_mean-1.3.0/lib/did_you_mean/formatters/plain_formatter.rb
   55 /Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/did_you_mean-1.3.0/lib/did_you_mean.rb
   56 /Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/re2-1.1.1/lib/re2.bundle
   57 /Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/re2-1.1.1/lib/re2/scanner.rb
   58 /Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/re2-1.1.1/lib/re2.rb

[NOTE]
You may have encountered a bug in the Ruby interpreter or extension libraries.
Bug reports are welcome.
For details: https://www.ruby-lang.org/bugreport.html

[IMPORTANT]
Don't forget to include the Crash Report log file under
DiagnosticReports directory in bug reports.

Abort trap: 6

Sometimes it fails:

ruby(46630,0x11203cdc0) malloc: can't allocate region
:*** mach_vm_map(size=140414988976128, flags: 60000100) failed (error code=3)
ruby(46630,0x11203cdc0) malloc: *** set a breakpoint in malloc_error_break to debug
ruby(46630,0x11203cdc0) malloc: can't allocate region
:*** mach_vm_map(size=140414988976128, flags: 60000100) failed (error code=3)
ruby(46630,0x11203cdc0) malloc: *** set a breakpoint in malloc_error_break to debug
Traceback (most recent call last):
re-test.rb: failed to allocate memory (NoMemoryError)

An objdump -d of the bundle file shows:

00000000000032f0 __ZL16re2_regexp_errorm:
    32f0: 55                            pushq   %rbp
    32f1: 48 89 e5                      movq    %rsp, %rbp
    32f4: 53                            pushq   %rbx
    32f5: 50                            pushq   %rax
    32f6: 48 89 fb                      movq    %rdi, %rbx
    32f9: be 0c 00 00 00                movl    $12, %esi
    32fe: e8 c9 30 00 00                callq   12489 <re2.cc+0x63cc>
    3303: 48 8b 43 20                   movq    32(%rbx), %rax
    3307: 48 8b 00                      movq    (%rax), %rax
    330a: 83 b8 80 00 00 00 00          cmpl    $0, 128(%rax)
    3311: 74 1e                         je      30 <__ZL16re2_regexp_errorm+0x41>
    3313: 48 8b 78 78                   movq    120(%rax), %rdi
    3317: 0f b6 37                      movzbl  (%rdi), %esi
    331a: 40 f6 c6 01                   testb   $1, %sil
    331e: 75 1d                         jne     29 <__ZL16re2_regexp_errorm+0x4d>
    3320: 48 ff c7                      incq    %rdi
    3323: 48 d1 ee                      shrq    %rsi
    3326: 48 83 c4 08                   addq    $8, %rsp
    332a: 5b                            popq    %rbx
    332b: 5d                            popq    %rbp
    332c: e9 49 31 00 00                jmp     12617 <re2.cc+0x647a>
    3331: b8 08 00 00 00                movl    $8, %eax
    3336: 48 83 c4 08                   addq    $8, %rsp
    333a: 5b                            popq    %rbx
    333b: 5d                            popq    %rbp
    333c: c3                            retq
    333d: 48 8b 77 08                   movq    8(%rdi), %rsi
    3341: 48 8b 7f 10                   movq    16(%rdi), %rdi
    3345: 48 83 c4 08                   addq    $8, %rsp
    3349: 5b                            popq    %rbx
    334a: 5d                            popq    %rbp
    334b: e9 2a 31 00 00                jmp     12586 <re2.cc+0x647a>
stanhu commented 4 years ago

Compiling with -fPIC did not seem to solve this problem.

mudge commented 4 years ago

Hi @stanhu,

Thanks for reporting this.

I'm having trouble reproducing it. I tried the following on macOS 10.15.4 and Ruby 2.7.1p83 (2020-03-31 revision a0c7c23c9c):

  url "https://github.com/google/re2/archive/2020-03-03.tar.gz"
  version "20200303"
  sha256 "04ee2aaebaa5038554683329afc494e684c30f82f2a1e47eb62450e59338f84d"
  # ...

  bottle do
    cellar :any
    sha256 "abb07cfb9def8d6f25a0d5694f0f23ba44b5b500f2d45d25d27515f62802c01b" => :catalina
    sha256 "92c4603fad274003e71699376b20a40c6e57c13f878774a894e28a9ef73295c1" => :mojave
    sha256 "58ac31c06c851bc5632c3a0703c0d41e55d8b015229cf03de09ee4a8701d92ef" => :high_sierra
  end
~> pry -rre2
[1] pry(main)> regexp = RE2::Regexp.new("{", log_errors: false)
=> #<RE2::Regexp /{/>
[2] pry(main)> puts regexp.error unless regexp.ok?
=> nil
  url "https://github.com/google/re2/archive/2020-04-01.tar.gz"
  version "20200401"
  sha256 "98794bc5416326817498384a9c43cbb5a406bab8da9f84f83c39ecad43ed5cea"
  # ...

  bottle do
    cellar :any
    sha256 "430e7efee518f5235ea75afc039168ce38c5407b525301199ccbc6c698300bb8" => :catalina
    sha256 "67eed1f38907d1d7318b5d95f035ca18ff6186d85ee690560b8b1cd387f8afd4" => :mojave
    sha256 "f111b7c906ab090d9db56e1f183f94d02e8cf5ce521e4ae4ff8e64d974cb5cbb" => :high_sierra
  end
~> pry -rre2
[1] pry(main)> regexp = RE2::Regexp.new("{", log_errors: false)
=> #<RE2::Regexp /{/>
[2] pry(main)> puts regexp.error unless regexp.ok?
=> nil

Looking at re2.bundle's links, they seem consistent between versions:

~/.gem/ruby/2.6.5/gems/re2-1.2.0/ext/re2> otool -L re2.bundle 
re2.bundle:
    /usr/local/opt/re2/lib/libre2.0.dylib (compatibility version 0.0.0, current version 0.0.0)
    /usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 902.1.0)
    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1281.100.1)

I know there is a breaking change coming in re2 (removing some deprecated APIs) so the SONAME has changed from 6 to 7, even upgrading to HEAD with Homebrew, your commands still pass for me:

[1] pry(main)> regexp = RE2::Regexp.new("{", log_errors: false)
=> #<RE2::Regexp /{/>
[2] pry(main)> puts regexp.error unless regexp.ok?
=> nil

The bigger picture question here is what is reasonable behaviour for the gem when the underlying library is upgraded? Especially if some internal re2 API changes (as RE2::Match did several years ago), I'm not sure what the gem can do if the linked library has shifted under its feet. We could avoid depending on the external library altogether and bundle re2 inside the gem (as Nokogiri does with libxml2) but this seems like a major, breaking change to the behaviour of the library.

Hopefully there's some smaller issue we can fix here.

stanhu commented 4 years ago

You may need to rewind further to https://github.com/Homebrew/homebrew-core/blob/549a5c81c67e401fd3c75339157b3b936ebbbbed/Formula/re2.rb.

The bigger picture question here is what is reasonable behaviour for the gem when the underlying library is upgraded? Especially if some internal re2 API changes (as RE2::Match did several years ago), I'm not sure what the gem can do if the linked library has shifted under its feet. We could avoid depending on the external library altogether and bundle re2 inside the gem (as Nokogiri does with libxml2) but this seems like a major, breaking change to the behaviour of the library.

That's a good question. We have a check for ffi and eventmachine that fails if the require step fails. Could we make re2 just fail fast on require 're2' if the underlying library changes? That would at least make the problem easier to diagnose.

mudge commented 4 years ago

Ah, that did the trick: compiling the gem against re2 2020-01-01 and then upgrading to 2020-04-01 causes the segfault when I call RE2::Regexp#error.

Looking at the diff between the two versions I see that there was a breaking ABI change and Google changed the soname from 0 to 6. (I see the latest release has changed again from 6 to 7).

However, it looks like Homebrew explicitly changes the soname of the library to 0 regardless of ABI version. Removing the lines from the re2 Formula that change the dylib ID and add symlinks stops Homebrew masquerading libre2.6 and libre2.7 as libre2.0 and causes the gem to fail fast as soon as you require it:

# Against re 2020-01-01 without the dylib ID changing or symlinks
~> gem install re2
Fetching re2-1.2.0.gem
Building native extensions. This could take a while...
Successfully installed re2-1.2.0
1 gem installed

~/.gem/ruby/2.6.5/gems/re2-1.2.0/ext/re2> otool -L re2.bundle 
re2.bundle:
    /usr/local/opt/re2/lib/libre2.0.dylib (compatibility version 0.0.0, current version 0.0.0)
    /usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 902.1.0)
    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1281.100.1)

# Upgrading to 2020-04-01 without the dylib ID changing or symlinks
~> brew upgrade re2
~/.gem/ruby/2.6.5/gems/re2-1.2.0/ext/re2> otool -L re2.bundle 
re2.bundle:
    /usr/local/opt/re2/lib/libre2.0.dylib (compatibility version 0.0.0, current version 0.0.0)
    /usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 902.1.0)
    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1281.100.1)

# Now libre2.0.dylib is entirely missing
~> ls -la /usr/local/opt/re2/lib
total 2168
drwxr-xr-x  7 mudge  admin     224  2 May 10:47 .
drwxr-xr-x  9 mudge  admin     288  2 May 10:47 ..
-r--r--r--  1 mudge  admin  348556  2 May 10:47 libre2.6.0.0.dylib
lrwxr-xr-x  1 mudge  admin      18  2 May 10:47 libre2.6.dylib -> libre2.6.0.0.dylib
-r--r--r--  1 mudge  admin  755048  2 May 10:47 libre2.a
lrwxr-xr-x  1 mudge  admin      18  2 May 10:47 libre2.dylib -> libre2.6.0.0.dylib
drwxr-xr-x  3 mudge  admin      96  2 May 10:47 pkgconfig

# Try requiring re2 linked against the now-missing re2 version
~/.gem/ruby/2.6.5/gems/re2-1.2.0/ext/re2> pry -rre2
Traceback (most recent call last):
    9: from /Users/mudge/.gem/ruby/2.7.1/bin/pry:23:in `<main>'
    8: from /Users/mudge/.gem/ruby/2.7.1/bin/pry:23:in `load'
    7: from /Users/mudge/.gem/ruby/2.7.1/gems/pry-0.13.1/bin/pry:12:in `<top (required)>'
    6: from /Users/mudge/.gem/ruby/2.7.1/gems/pry-0.13.1/lib/pry/cli.rb:89:in `parse_options'
    5: from /Users/mudge/.gem/ruby/2.7.1/gems/pry-0.13.1/lib/pry/pry_class.rb:146:in `final_session_setup'
    4: from /Users/mudge/.gem/ruby/2.7.1/gems/pry-0.13.1/lib/pry/pry_class.rb:104:in `load_requires'
    3: from /Users/mudge/.gem/ruby/2.7.1/gems/pry-0.13.1/lib/pry/pry_class.rb:104:in `each'
    2: from /Users/mudge/.gem/ruby/2.7.1/gems/pry-0.13.1/lib/pry/pry_class.rb:105:in `block in load_requires'
    1: from /Users/mudge/.rubies/ruby-2.7.1/lib/ruby/2.7.0/rubygems/core_ext/kernel_require.rb:92:in `require'
/Users/mudge/.rubies/ruby-2.7.1/lib/ruby/2.7.0/rubygems/core_ext/kernel_require.rb:92:in `require': cannot load such file -- re2 (LoadError)
    13: from /Users/mudge/.gem/ruby/2.7.1/bin/pry:23:in `<main>'
    12: from /Users/mudge/.gem/ruby/2.7.1/bin/pry:23:in `load'
    11: from /Users/mudge/.gem/ruby/2.7.1/gems/pry-0.13.1/bin/pry:12:in `<top (required)>'
    10: from /Users/mudge/.gem/ruby/2.7.1/gems/pry-0.13.1/lib/pry/cli.rb:89:in `parse_options'
     9: from /Users/mudge/.gem/ruby/2.7.1/gems/pry-0.13.1/lib/pry/pry_class.rb:146:in `final_session_setup'
     8: from /Users/mudge/.gem/ruby/2.7.1/gems/pry-0.13.1/lib/pry/pry_class.rb:104:in `load_requires'
     7: from /Users/mudge/.gem/ruby/2.7.1/gems/pry-0.13.1/lib/pry/pry_class.rb:104:in `each'
     6: from /Users/mudge/.gem/ruby/2.7.1/gems/pry-0.13.1/lib/pry/pry_class.rb:105:in `block in load_requires'
     5: from /Users/mudge/.rubies/ruby-2.7.1/lib/ruby/2.7.0/rubygems/core_ext/kernel_require.rb:156:in `require'
     4: from /Users/mudge/.rubies/ruby-2.7.1/lib/ruby/2.7.0/rubygems/core_ext/kernel_require.rb:168:in `rescue in require'
     3: from /Users/mudge/.rubies/ruby-2.7.1/lib/ruby/2.7.0/rubygems/core_ext/kernel_require.rb:168:in `require'
     2: from /Users/mudge/.gem/ruby/2.7.1/gems/re2-1.2.0/lib/re2.rb:6:in `<top (required)>'
     1: from /Users/mudge/.rubies/ruby-2.7.1/lib/ruby/2.7.0/rubygems/core_ext/kernel_require.rb:92:in `require'
/Users/mudge/.rubies/ruby-2.7.1/lib/ruby/2.7.0/rubygems/core_ext/kernel_require.rb:92:in `require': dlopen(/Users/mudge/.gem/ruby/2.7.1/gems/re2-1.2.0/lib/re2.bundle, 9): Library not loaded: /usr/local/opt/re2/lib/libre2.0.dylib (LoadError)
  Referenced from: /Users/mudge/.gem/ruby/2.7.1/gems/re2-1.2.0/lib/re2.bundle

Looking on Ubuntu, it seems the libre2-dev and libre2-5 package correctly suffix the shared libraries with the soname so I suspect this is a macOS and Homebrew issue only.

If I raise a pull request against Homebrew to stop overwriting the ABI version, this means the gem would fail fast if you try to require it when the underlying libre2 ABI version has changed and seems the most reasonable approach.

This wouldn't require any change to the gem itself.

What do you think?

mudge commented 4 years ago

This has now been merged into Homebrew core; could you please re-test with the latest formula (2020-05-01 revision 1) and let me know if it behaves as you expect?

Perhaps use the 2020-04-01 formula to install re2 with a soname of 0 (though it should be 6):

Then install and compile the gem against that version with gem install re2 and confirm it works as expected:

$ irb -rre2
> RE2('{').error
=> nil

Then upgrade to the latest, fixed version of the re2 Formula (which has a soname of 7) and try to run the same command again:

~> irb -rre2
/Users/mudge/.rubies/ruby-2.7.1/lib/ruby/2.7.0/irb/init.rb:290: warning: LoadError: dlopen(/Users/mudge/.gem/ruby/2.7.1/gems/re2-1.2.0/lib/re2.bundle, 9): Library not loaded: /usr/local/opt/re2/lib/libre2.0.dylib
  Referenced from: /Users/mudge/.gem/ruby/2.7.1/gems/re2-1.2.0/lib/re2.bundle
  Reason: image not found - /Users/mudge/.gem/ruby/2.7.1/gems/re2-1.2.0/lib/re2.bundle
stanhu commented 4 years ago

@mudge Awesome, that worked for me as well. Thanks so much!

stanhu commented 4 years ago

Maybe I should create a separate issue, but does gem pristine re2 work for you now? I am seeing:

$ gem pristine re2
Restoring gems to pristine condition...
Building native extensions. This could take a while...
ERROR:  While executing gem ... (Gem::Ext::BuildError)
    ERROR: Failed to build gem native extension.

    current directory: /Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/re2-1.1.1/ext/re2
/Users/stanhu/.rbenv/versions/2.6.5/bin/ruby -I /Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/2.6.0 -r ./siteconf20200502-7576-t86j84.rb extconf.rb
checking for -lstdc++... yes
checking for stdint.h... yes
checking for rb_str_sublen()... yes
checking for -lre2... yes
checking for re2 requires C++11 compiler... yes
checking for RE2::Match() with endpos argument... yes
creating Makefile

current directory: /Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/re2-1.1.1/ext/re2
make "DESTDIR=" clean

current directory: /Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/re2-1.1.1/ext/re2
make "DESTDIR="
compiling re2.cc
In file included from re2.cc:9:
In file included from /Users/stanhu/.rbenv/versions/2.6.5/include/ruby-2.6.0/ruby.h:33:
In file included from /Users/stanhu/.rbenv/versions/2.6.5/include/ruby-2.6.0/ruby/ruby.h:2111:
/Users/stanhu/.rbenv/versions/2.6.5/include/ruby-2.6.0/ruby/intern.h:56:19: warning: 'register' storage class specifier is deprecated and incompatible with C++17 [-Wdeprecated-register]
void rb_mem_clear(register VALUE*, register long);
                  ^~~~~~~~~
/Users/stanhu/.rbenv/versions/2.6.5/include/ruby-2.6.0/ruby/intern.h:56:36: warning: 'register' storage class specifier is deprecated and incompatible with C++17 [-Wdeprecated-register]
void rb_mem_clear(register VALUE*, register long);
                                   ^~~~~~~~~
re2.cc:261:37: error: no member named 'utf8' in 're2::RE2::Options'
              p->pattern->options().utf8() ? "UTF-8" : "ISO-8859-1"));
              ~~~~~~~~~~~~~~~~~~~~~ ^
re2.cc:37:36: note: expanded from macro 'ENCODED_STR_NEW'
      int _enc = rb_enc_find_index(encoding); \
                                   ^~~~~~~~
re2.cc:459:35: error: no member named 'utf8' in 're2::RE2::Options'
            p->pattern->options().utf8() ? "UTF-8" : "ISO-8859-1"));
            ~~~~~~~~~~~~~~~~~~~~~ ^
re2.cc:37:36: note: expanded from macro 'ENCODED_STR_NEW'
      int _enc = rb_enc_find_index(encoding); \
                                   ^~~~~~~~
re2.cc:483:33: error: no member named 'utf8' in 're2::RE2::Options'
          p->pattern->options().utf8() ? "UTF-8" : "ISO-8859-1");
          ~~~~~~~~~~~~~~~~~~~~~ ^
re2.cc:37:36: note: expanded from macro 'ENCODED_STR_NEW'
      int _enc = rb_enc_find_index(encoding); \
                                   ^~~~~~~~
re2.cc:614:29: error: no member named 'utf8' in 're2::RE2::Options'
      p->pattern->options().utf8() ? "UTF-8" : "ISO-8859-1");
      ~~~~~~~~~~~~~~~~~~~~~ ^
re2.cc:37:36: note: expanded from macro 'ENCODED_STR_NEW'
      int _enc = rb_enc_find_index(encoding); \
                                   ^~~~~~~~
re2.cc:687:19: error: no member named 'set_utf8' in 're2::RE2::Options'
      re2_options.set_utf8(RTEST(utf8));
      ~~~~~~~~~~~ ^
re2.cc:770:29: error: no member named 'utf8' in 're2::RE2::Options'
      p->pattern->options().utf8() ? "UTF-8" : "ISO-8859-1");
      ~~~~~~~~~~~~~~~~~~~~~ ^
re2.cc:37:36: note: expanded from macro 'ENCODED_STR_NEW'
      int _enc = rb_enc_find_index(encoding); \
                                   ^~~~~~~~
re2.cc:788:29: error: no member named 'utf8' in 're2::RE2::Options'
      p->pattern->options().utf8() ? "UTF-8" : "ISO-8859-1");
      ~~~~~~~~~~~~~~~~~~~~~ ^
re2.cc:37:36: note: expanded from macro 'ENCODED_STR_NEW'
      int _enc = rb_enc_find_index(encoding); \
                                   ^~~~~~~~
re2.cc:818:42: error: no member named 'utf8' in 're2::RE2::Options'
  return BOOL2RUBY(p->pattern->options().utf8());
                   ~~~~~~~~~~~~~~~~~~~~~ ^
re2.cc:21:23: note: expanded from macro 'BOOL2RUBY'
#define BOOL2RUBY(v) (v ? Qtrue : Qfalse)
                      ^
re2.cc:1015:31: error: no member named 'utf8' in 're2::RE2::Options'
        p->pattern->options().utf8() ? "UTF-8" : "ISO-8859-1");
        ~~~~~~~~~~~~~~~~~~~~~ ^
re2.cc:37:36: note: expanded from macro 'ENCODED_STR_NEW'
      int _enc = rb_enc_find_index(encoding); \
                                   ^~~~~~~~
re2.cc:1046:39: error: no member named 'utf8' in 're2::RE2::Options'
      BOOL2RUBY(p->pattern->options().utf8()));
                ~~~~~~~~~~~~~~~~~~~~~ ^
re2.cc:21:23: note: expanded from macro 'BOOL2RUBY'
#define BOOL2RUBY(v) (v ? Qtrue : Qfalse)
                      ^
re2.cc:1116:33: error: no member named 'utf8' in 're2::RE2::Options'
          p->pattern->options().utf8() ? "UTF-8" : "ISO-8859-1"),
          ~~~~~~~~~~~~~~~~~~~~~ ^
re2.cc:37:36: note: expanded from macro 'ENCODED_STR_NEW'
      int _enc = rb_enc_find_index(encoding); \
                                   ^~~~~~~~
re2.cc:1287:31: error: no member named 'utf8' in 're2::RE2::Options'
        p->pattern->options().utf8() ? "UTF-8" : "ISO-8859-1");
        ~~~~~~~~~~~~~~~~~~~~~ ^
re2.cc:37:36: note: expanded from macro 'ENCODED_STR_NEW'
      int _enc = rb_enc_find_index(encoding); \
                                   ^~~~~~~~
re2.cc:1324:31: error: no member named 'utf8' in 're2::RE2::Options'
        p->pattern->options().utf8() ? "UTF-8" : "ISO-8859-1");
        ~~~~~~~~~~~~~~~~~~~~~ ^
re2.cc:37:36: note: expanded from macro 'ENCODED_STR_NEW'
      int _enc = rb_enc_find_index(encoding); \
                                   ^~~~~~~~
2 warnings and 13 errors generated.
make: *** [re2.o] Error 1

make failed, exit code 2

Gem files will remain installed in /Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/re2-1.1.1 for inspection.
Results logged to /Users/stanhu/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/extensions/x86_64-darwin-19/2.6.0/re2-1.1.1/gem_make.out
mudge commented 4 years ago

I think you need to update to re2 1.2.0 as 2020-05-01 removed the utf8 option (hence the soname bump): see https://github.com/mudge/re2/issues/40.

stanhu commented 4 years ago

@mudge Once again, thanks! That did the trick.