Skip to content

Commit 587581e

Browse files
committed
work around Ruby 3.2 linking conventions on Darwin
See #87
1 parent 1c0e91d commit 587581e

File tree

1 file changed

+67
-0
lines changed

1 file changed

+67
-0
lines changed

test/rcd_test/ext/mri/extconf.rb

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,73 @@
2323
# https://github.com/rake-compiler/rake-compiler-dock/issues/69
2424
puts "Linking with '-static' flag"
2525
$LDFLAGS << ' -static'
26+
else
27+
if RbConfig::CONFIG["target_os"].include?("darwin")
28+
## In ruby 3.2, symbol resolution changed on Darwin, to introduce the `-bundle_loader` flag.
29+
##
30+
## See https://github.com/rake-compiler/rake-compiler-dock/issues/87 for a lot of context, but
31+
## I'll try to summarize here.
32+
##
33+
## > -bundle_loader executable
34+
## > This specifies the executable that will be loading the bundle output file being linked.
35+
## > Undefined symbols from the bundle are checked against the specified executable like it
36+
## > was one of the dynamic libraries the bundle was linked with.
37+
##
38+
## There are good reasons to do this, including faster initialiation/loading as the Darwin
39+
## toolchain gets improved over time.
40+
##
41+
## Unfortunately, this flag prevents us from building a shared object that works with both a
42+
## Ruby compiled with `--enable-shared` and one compiled with `--disabled-shared`. The result
43+
## is usually an "Symbol not found" error about `_rb_cObject`, or a "dyld: missing symbol
44+
## called" error.
45+
##
46+
## There are two workarounds that I know of (there may be others I don't know of), and
47+
## they are ...
48+
49+
## ----------------------------------------
50+
## SOLUTION 1, the `-flat_namespace` flag
51+
##
52+
## > Two-level namespace
53+
## > By default all references resolved to a dynamic library record the library to which
54+
## > they were resolved. At runtime, dyld uses that information to directly resolve symbols.
55+
## > The alternative is to use the -flat_namespace option. With flat namespace, the library
56+
## > is not recorded. At runtime, dyld will search each dynamic library in load order when
57+
## > resolving symbols. This is slower, but more like how other operating systems resolve
58+
## > symbols. Two-level namespace By default all references resolved to a dynamic library
59+
## > record the library to which they were resolved. At runtime, dyld uses that information
60+
## > to directly resolve symbols. The alternative is to use the -flat_namespace option.
61+
## > With flat namespace, the library is not recorded. At runtime, dyld will search each
62+
## > dynamic library in load order when resolving symbols. This is slower, but more like how
63+
## > other operating systems resolve symbols.
64+
##
65+
#
66+
# puts "Adding '-flat_namespace'"
67+
# $LDFLAGS << ' -flat_namespace'
68+
#
69+
## This solution unfortunately introduces new behavior that any symbols statically linked into
70+
## the shared object (e.g., libxml2 in nokogiri.bundle) may not be resolved locally from the
71+
## shared object, but instead resolved from a shared object loaded in the main process.
72+
##
73+
## This solution might be good for you if:
74+
## - you don't statically link things into your bundle,
75+
## - or you don't export those symbols,
76+
## - or you can avoid exporting those symbols (e.g., by using `-load_hidden`, or
77+
## `-exported_symbols_list` or some other mechanism)
78+
##
79+
80+
## ----------------------------------------
81+
## BUT ... if that is a problem, try SOLUTION 2, remove the `-bundle-loader` flag
82+
##
83+
## This returns us to the symbol resolution we had in previous Rubies. It feels gross but may
84+
## be a workaround for gem maintainers until we all figure out a better way to deal with this.
85+
extdldflags = RbConfig::MAKEFILE_CONFIG["EXTDLDFLAGS"].split
86+
if found = extdldflags.index("-bundle_loader")
87+
removed_1 = extdldflags.delete_at(found) # flag
88+
removed_2 = extdldflags.delete_at(found) # and its argument
89+
puts "Removing '#{removed_1} #{removed_2}' from EXTDLDFLAGS"
90+
end
91+
RbConfig::MAKEFILE_CONFIG["EXTDLDFLAGS"] = extdldflags.join(" ")
92+
end
2693
end
2794

2895
create_makefile("rcd_test/rcd_test_ext")

0 commit comments

Comments
 (0)