Keep build-tree details out of native gems - #16
Merged
Merged
Conversation
Three things a gem compiled against these Rubies picked up from the build: - mkmf links have_func probes against libruby-static.a, where Ruby's internal functions are linkable, so a probe for one succeeds and the gem then fails to load: strscan 3.1.8 on Ruby 2.4-2.7 with 'undefined symbol: rb_deprecate_constant'. Merge the archive into one object and localize its hidden symbols, leaving the exported API, as a shared libruby would. - rbconfig's LDFLAGS and configure_args named the build's dependency directories, so gems got -I, -L and a run path into /work/.build. Scrub the run path flags, point --with-*-dir at the relocated prefix (where those headers and libraries now are), and remove the same run paths from the shipped binaries. - The bundled ncurses looked for terminfo under the build directory, so every readline load printed 'Cannot read termcap database'. Use the system's. The installation test now fails if rbconfig or a binary's run path names the build tree, or if the static libruby still has linkable hidden symbols.
excid3
added a commit
that referenced
this pull request
Sep 30, 2026
Move the Rails table out of the end-of-life section and extend it to the supported series, add a Native gems section for what #16 arranged (static libruby's linkable symbols, rbconfig paths, terminfo), and put the sections for people using the builds ahead of the ones for building them.
This was referenced Sep 30, 2026
excid3
added a commit
that referenced
this pull request
Sep 30, 2026
…g files Merging libruby-static.a into one object (#16) made every mkmf probe link the whole of Ruby with its debug info: ~900 MB of linker memory per have_func on 3.4, which got four-way parallel gem installs OOM-killed in a 2 GB container, and a killed link reads as 'function missing' (json 3.0.2 then failed to compile). Nothing debugs through this archive; stripped, a probe takes ~50 MB (the published 2.17 builds took ~270 MB) and the archive shrinks from 156 MB to 15 MB. The installation test fails if it has debug sections again. The pkg-config files copied from the dependencies and Ruby's ruby-X.pc named the build tree; point them at ${pcfiledir}/../.. like the rest, and check it. Also blank rbconfig's RUST_LIB, a build-tree path only Ruby's own build uses.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Found by deploying fresh Rails apps (3.2 to 8.0) on the published builds of every series. All 17 deploys passed, but three things leak from the build into gems compiled on the server.
1. Gems that build but can't load (static libruby)
gem install strscan -v 3.1.8on 2.4.10, 2.5.9, 2.6.10 and 2.7.8:mkmf links
have_funcprobes againstlibruby-static.a, where Ruby's internal (hidden-visibility) functions are ordinary linkable globals. The probe succeeds, the gem calls the function, and therubyexecutable doesn't export it. A freshbundleof Rails 6.1 on 2.5.9 hits this through mail -> net-imap -> strscan.Fix: after install, merge the archive into a single relocatable object and
objcopy --localize-hiddenit. Only the exported API stays linkable, as with a shared libruby.2. Build paths in rbconfig and run paths
LDFLAGS/DLDFLAGScarried-Wl,-rpath,/work/.build/.../deps/ncurses/lib(-Wl,-Ron older series), which went into every native gem.configure_argskept--with-openssl-dir=/work/.build/...etc., which mkmf'sdir_configturns into-I/-L/run path for gems such as puma. They now point at the relocated prefix, where those headers and static libraries actually are.rubyand extensions had the same run paths; removed with patchelf (present in the manylinux images).3. terminfo
The bundled ncurses searched a terminfo database under the build directory:
Cannot read termcap database; using dumb terminal settings.on every readline load. It now reads/etc/terminfo:/lib/terminfo:/usr/share/terminfo.Checks
check_no_build_paths!fails the build if rbconfig or any binary's run path names the build's dependency tree or install prefix, or iflibruby-static.astill has global hidden symbols.Testing
Built locally for arm64: 1.8.7-p374, 2.1.10, 2.5.9, 2.7.8, 3.1.7 (no YJIT) and 3.2.0, 3.4.11, 4.0.7 (YJIT). On Ubuntu 24.04 with each:
/work/in rbconfig; no run path on shipped binaries--with-openssl-dir, no dynamic libssl dependency, no exported SSL symbolsNot changed (same in the published builds): 4.0.7's
bin/rubyhas an/./libRPATH and rbconfig keeps aRUST_LIBmakefile variable pointing at the build tree.After merge
libexec/package.rbis in every fingerprint, and this one does change every build: dispatch Release New Versions withreplace_all.