Bug #19005
closedRuby interpreter compiled XCode 14 cannot build some native gems on macOS
Description
This seems related to https://bugs.ruby-lang.org/issues/18912 and https://bugs.ruby-lang.org/issues/18981 .
Steps to reproduce:
- Upgrade to XCode 14.
- Compile a new Ruby interpreter. I used the version provided in https://github.com/ruby/ruby/pull/6297 with
./configure --prefix=/tmp/ruby --with-openssl-dir=$(brew --prefix openssl@1.1) --with-readline-dir=$(brew --prefix readline) --enable-shared
. - Confirm that
-Wl,-undefined,dynamic_lookup
is no longer available:
irb(main):001:0> RbConfig::CONFIG['DLDFLAGS']
=> "-Wl,-multiply_defined,suppress"
- Ran
gem install pg_query
(gem install ffi-yajl
will also fail).
Error:
linking shared-object pg_query/pg_query.bundle
Undefined symbols for architecture arm64:
"Init_pg_query", referenced from:
-exported_symbol[s_list] command line option
(maybe you meant: _Init_pg_query)
ld: symbol(s) not found for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
I can workaround the problem by doing:
gem install pg_query -- --with-ldflags="-Wl,-undefined,dynamic_lookup"
Updated by hsbt (Hiroshi SHIBATA) over 2 years ago
- Related to Bug #18912: Build failure with Xcode 14 and macOS 13 (Ventura) Beta added
- Related to Bug #18981: Backport bundle loader option to stable versions added
Updated by stanhu (Stan Hu) over 2 years ago
I should note that it's also possible to workaround the issue by compiling the interpreter with DLDFLAGS="-Wl,-undefined,dynamic_lookup"
.
configure
no longer adds this flag by default because of the warning:
configure:28712: checking whether -Wl,-undefined,dynamic_lookup is accepted as LDFLAGS
configure:28739: gcc -o conftest -g -O2 -Wall -Wextra -Wdeprecated-declarations -Wdivision-by-zero -Wimplicit-function-declaration -Wimplicit-int -Wmisleading-indentation -Wpointer-arith -Wshorten-64-to-32 -Wwrite-strings -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wunused-variable -Wextra-tokens -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT -fstack-protector-strong -Wl,-undefined,dynamic_lookup conftest.c -lpthread -ldl -lobjc >&5
ld: warning: -undefined dynamic_lookup may not work with chained fixups
Updated by stanhu (Stan Hu) over 2 years ago
It's possible this is a problem with the respective gems. In https://github.com/pganalyze/pg_query/issues/255#issuecomment-1248800937, I note that the Init_pg_query
is using C++ visibility rules.
However, for ffi-yajl
, I'm not sure this is the same problem:
compiling encoder.c
encoder.c:307:14: warning: unused function 'rb_cBignum_ffi_yajl' [-Wunused-function]
static VALUE rb_cBignum_ffi_yajl(VALUE self, VALUE rb_yajl_gen, VALUE state) {
^
1 warning generated.
linking shared-object ffi_yajl/ext/encoder.bundle
Undefined symbols for architecture arm64:
"_yajl_gen_alloc", referenced from:
_mEncoder_do_yajl_encode in encoder.o
"_yajl_gen_array_close", referenced from:
_gen_array_close in encoder.o
"_yajl_gen_array_open", referenced from:
_gen_array_open in encoder.o
"_yajl_gen_bool", referenced from:
_gen_true in encoder.o
_gen_false in encoder.o
"_yajl_gen_config", referenced from:
_mEncoder_do_yajl_encode in encoder.o
"_yajl_gen_free", referenced from:
_mEncoder_do_yajl_encode in encoder.o
"_yajl_gen_get_buf", referenced from:
_mEncoder_do_yajl_encode in encoder.o
"_yajl_gen_map_close", referenced from:
_gen_map_close in encoder.o
"_yajl_gen_map_open", referenced from:
_gen_map_open in encoder.o
"_yajl_gen_null", referenced from:
_gen_null in encoder.o
"_yajl_gen_number", referenced from:
_gen_number in encoder.o
"_yajl_gen_string", referenced from:
_gen_cstring in encoder.o
ld: symbol(s) not found for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make: *** [encoder.bundle] Error 1
Updated by stanhu (Stan Hu) over 2 years ago
ffi-yajl
expects to run dlopen
to bring in these symbols: https://github.com/chef/ffi-yajl/blob/2ac7f207c0850a7621880ea03c85c509072fff85/ext/ffi_yajl/ext/dlopen/dlopen.c#L29
Updated by nobu (Nobuyoshi Nakada) over 2 years ago
- Status changed from Open to Feedback
What is the version of ruby you are using?
Updated by stanhu (Stan Hu) over 2 years ago
nobu (Nobuyoshi Nakada) wrote in #note-5:
What is the version of ruby you are using?
ruby 2.7.6p219 (2022-04-12 revision 44c8bfa984) [arm64-darwin21]
Updated by stanhu (Stan Hu) over 2 years ago
- ruby -v set to ruby 2.7.6p219 (2022-04-12 revision 44c8bfa984) [arm64-darwin21]
Updated by stanhu (Stan Hu) over 2 years ago
A related issue was discussed in https://gitlab.kitware.com/vtk/vtk/-/issues/17214#note_385533 in the context of Python.
https://github.com/apple-opensource/ld64/pull/1/files would indeed be useful.
Updated by stanhu (Stan Hu) over 2 years ago
I think we can close this issue, unless someone thinks we might want to relax the configure
check so that the -undefined,dynamic_lookup
linker flag is again the default for macOS.
Gems should be able to handle building without -undefined,dynamic_lookup
. Here are related fixes:
Updated by hsbt (Hiroshi SHIBATA) over 2 years ago
- Status changed from Feedback to Open
ruby_3_1
head already fixed a similar issue with bundle loader. But I could reproduce https://bugs.ruby-lang.org/issues/19005#note-3 with ruby_3_1
head.
We should fix this before 3.1.3 release.
Updated by hsbt (Hiroshi SHIBATA) over 2 years ago
Ah, Sorry. I misunderstood. I could install other native extensions like ffi, hiredis and eventmachine.
this issue happens like ffi-yajl.
Updated by stanhu (Stan Hu) over 2 years ago
Yes, it's only a few gems that previously depended on -undefined,dynamic_lookup
being present as a linker argument.
In the pg_query
case, it seems that there is a bug: the library exports a symbol that's not actually present.
In the ffi-yajl
case, it seems that the gem expects to dlopen()
the yayl
library, so the symbols are not present until the extension is actually loaded. We can resolve that by explicitly marking the symbols that will be used dynamically.
However, it's interesting to me that the Ruby configure
script returned 0 compiling the program, but still marked the flag as being unavailable:
configure:28712: checking whether -Wl,-undefined,dynamic_lookup is accepted as LDFLAGS
configure:28739: gcc -o conftest -g -O2 -Wall -Wextra -Wdeprecated-declarations -Wdivision-by-zero -Wimplicit-function-declaration -Wimplicit-int -Wmisleading-indentation -Wpointer-arith -Wshorten-\
64-to-32 -Wwrite-strings -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-parentheses-equality -W\
no-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wunused-variable -Wextra-tokens -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT -fstack-\
protector-strong -Wl,-undefined,dynamic_lookup conftest.c -lpthread -ldl -lobjc >&5
ld: warning: -undefined dynamic_lookup may not work with chained fixups
configure:28739: $? = 0
configure: failed program was:
I think it's because configure
expects no output:
(eval "$ac_link") 2>conftest.err
ac_status=$?
if test -s conftest.err; then
grep -v '^ *+' conftest.err >conftest.er1
cat conftest.er1 >&5
mv -f conftest.er1 conftest.err
fi
I don't know if there's a way to suppress that warning.
Updated by nobu (Nobuyoshi Nakada) over 2 years ago
- Status changed from Open to Closed
Applied in changeset git|6898984f1cd1b0375c3da44d7832724489e0e470.
[Bug #19005] dynamic_lookup linker option in external libraries
The warning against -undefined dynamic_lookup
is just a warning yet,
and many gems seem to pay no attention to warnings. Until it fails
actually, keep it as a migration path, except for standard extension
libraries and bundled extension gems.
Updated by stanhu (Stan Hu) over 2 years ago
Thanks! I've confirmed this solves the problem and that the linker adds this argument.
Could this patch be backported to 2.7 as well? Thanks!
Updated by nobu (Nobuyoshi Nakada) over 2 years ago
- Backport changed from 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN to 2.7: REQUIRED, 3.0: REQUIRED, 3.1: REQUIRED
Updated by hsbt (Hiroshi SHIBATA) about 2 years ago
Could this patch be backported to 2.7 as well? Thanks!
@stanhu (Stan Hu) Maybe, yes. I submitted the backport patches for Ruby 2.7 at https://github.com/ruby/ruby/pull/6442.
Updated by nagachika (Tomoyuki Chikanaga) about 2 years ago
- Backport changed from 2.7: REQUIRED, 3.0: REQUIRED, 3.1: REQUIRED to 2.7: REQUIRED, 3.0: REQUIRED, 3.1: DONE
Updated by hsbt (Hiroshi SHIBATA) about 2 years ago
- Backport changed from 2.7: REQUIRED, 3.0: REQUIRED, 3.1: DONE to 2.7: DONE, 3.0: DONE, 3.1: DONE
https://github.com/ruby/ruby/pull/6441 and https://github.com/ruby/ruby/pull/6442 have been merged.
Updated by Eregon (Benoit Daloze) about 2 years ago
I think longer-term it would be really best to avoid using -Wl,-undefined,dynamic_lookup
.
The gems which depend on that should be fixed to avoid the need for it, or add -Wl,-undefined,dynamic_lookup
themselves if they truly need it (probably extremely rare, it most likely means a missing -lmylib
or so).
Updated by kou (Kouhei Sutou) about 2 years ago
FYI: Here is one of my use cases that need -Wl,-undefined,dynamic_lookup
:
- cairo-gobject gem https://github.com/ruby-gnome/ruby-gnome/tree/master/cairo-gobject depends on symbols in cairo gem https://github.com/rcairo/rcairo/ and glib2 gem https://github.com/ruby-gnome/ruby-gnome/tree/master/glib2 .
Updated by kou (Kouhei Sutou) about 2 years ago
Ah, should we use -Wl,-U
with explicit listed depending symbols for this?
Updated by stanhu (Stan Hu) about 2 years ago
I think longer-term it would be really best to avoid using -Wl,-undefined,dynamic_lookup.
Yeah, I agree. The problem is that most gems aren't tested in this mode, and it's long been the default everywhere.
Ah, should we use
-Wl,-U
with explicit listed depending symbols for this?
I did that with https://github.com/chef/ffi-yajl/pull/114 since a dlopen
call is used, but in your case you can you just link the library directly?
Updated by kou (Kouhei Sutou) about 2 years ago
but in your case you can you just link the library directly?
I can't do it because cairo.bundle
(it's a bundle not a shared library) in the cairo gem can't be linked from cairo_gobject.bundle
in the cairo-gobject gem.
Updated by naruse (Yui NARUSE) almost 2 years ago
I found a video which explains chained fixups:
https://developer.apple.com/videos/play/wwdc2022/110362/
Updated by stanhu (Stan Hu) almost 2 years ago
I think this problem was "accidentally" fixed in Ruby 2.7.7 and 3.0.5, but it's not working in Ruby 3.1.3 and up due to a simple removal of a dollar sign (https://github.com/ruby/ruby/commit/667aa81219ca080c0a4b9f97d29bb3221bd08a33).
In Ruby 3.1.3, I'm not seeing the ADDITIONAL_DLDFLAGS
set with -Wl,-undefined,dynamic_lookup
:
irb(main):001:0> RUBY_VERSION
=> "3.1.3"
irb(main):002:0> RbConfig::CONFIG['ADDITIONAL_DLDFLAGS']
=> ""
Whereas 3.0.5 has this:
irb(main):001:0> RUBY_VERSION
=> "3.0.5"
irb(main):002:0> RbConfig::CONFIG['ADDITIONAL_DLDFLAGS']
=> "-Wl,-undefined,dynamic_lookup"
If I look at the ./configure
output in Ruby 3.0.5 or 2.7.7, I see something like this:
checking whether -Wl,-undefined,dynamic_lookup is accepted as LDFLAGS... ./configure: line 29806: -Wl,-undefined,dynamic_lookup=: command not found
checking whether -Wl,-undefined,dynamic_lookup is accepted for bundle... no
But with 3.1.3 and up, the LDFLAGS
check is a straight no:
checking whether -Wl,-undefined,dynamic_lookup is accepted as LDFLAGS... no
The configure
output with Ruby 3.0.5 looks like:
if ac_fn_c_try_link "$LINENO"
then :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
printf "%s\n" "${msg_result_yes}yes${msg_reset}" >&6 ; }
else $as_nop
$flag=
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
printf "%s\n" "${msg_result_no}no${msg_reset}" >&6 ; }
fi
The failing line in question is $flag=
.
In Ruby 3.1.3 and up, it appears $flag=
has been replaced with flag=
due to https://github.com/ruby/ruby/commit/667aa81219ca080c0a4b9f97d29bb3221bd08a33:
if ac_fn_c_try_link "$LINENO"
then :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
printf "%s\n" "${msg_result_yes}yes${msg_reset}" >&6 ; }
else $as_nop
flag=
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
printf "%s\n" "${msg_result_no}no${msg_reset}" >&6 ; }
fi
Updated by stanhu (Stan Hu) almost 2 years ago
- Status changed from 9 to Closed
Ok, I see this was reported in https://bugs.ruby-lang.org/issues/19082.
Updated by Eregon (Benoit Daloze) almost 2 years ago
- Related to Bug #19082: Recent gRPC gem fails to build from the source in already released versions added
Updated by stanhu (Stan Hu) almost 2 years ago
A possible fix is to squelch this warning via -no_fixup_chains
:
diff --git a/configure.ac b/configure.ac
index a2a0c1e387..d653a13bb8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3104,7 +3104,7 @@ AS_IF([test "$rb_cv_dlopen" = yes], [
AC_SUBST(ADDITIONAL_DLDFLAGS, "")
for flag in \
"-multiply_defined suppress" \
- "-undefined dynamic_lookup" \
+ "-no_fixup_chains -undefined dynamic_lookup" \
; do
test "x${linker_flag}" = x || flag="${linker_flag}`echo ${flag} | tr ' ' ,`"
RUBY_TRY_LDFLAGS([$flag], [], [flag=])
I should note that bazel dropped -undefined dynamic_lookup
entirely in https://github.com/bazelbuild/bazel/issues/16413, but that will break some gems that are not ready for this.
Updated by stanhu (Stan Hu) almost 2 years ago
Submitted https://github.com/ruby/ruby/pull/7086.
Updated by naruse (Yui NARUSE) almost 2 years ago
- Status changed from 9 to Open
Updated by nobu (Nobuyoshi Nakada) almost 2 years ago
Adding -no_fixup_chains -undefined dynamic_lookup
together will break old compilers.
-no_fixup_chains
should be checked solely before -undefined dynamic_lookup
, at least.
Updated by stanhu (Stan Hu) almost 2 years ago
nobu (Nobuyoshi Nakada) wrote in #note-33:
Adding
-no_fixup_chains -undefined dynamic_lookup
together will break old compilers.
-no_fixup_chains
should be checked solely before-undefined dynamic_lookup
, at least.
Good point. I've updated https://github.com/ruby/ruby/pull/7086.
Updated by stanhu (Stan Hu) over 1 year ago
We can close this now that XCode 14.3, which ships with clang 14.0.3, now fixes this issue: https://github.com/python/cpython/issues/97524#issuecomment-1458855301
Updated by jeremyevans0 (Jeremy Evans) over 1 year ago
- Status changed from Open to Closed