sparklemotion/sqlite3-ruby

"symbol not found" on alpine 3.19

Closed this issue · 12 comments

TL;DR from the maintainers -- read me first!

You can work around this issue in three ways:

  1. upgrade to sqlite3 >= v2.0.0
  2. pin to alpine 3.18
  3. compile the gem from source, see INSTALLATION.md

(The original bug description follows)


On the latest ruby image with alpine 3.19, I can install sqlite3 ruby gem, but it emits "symbol not found" errors at runtime.

This is a quick test on alpine 3.18:

$ docker run --rm -it ruby:3.2.2-alpine3.18 /bin/sh
/ # gem install sqlite3
Fetching sqlite3-1.6.9-x86_64-linux.gem
Successfully installed sqlite3-1.6.9-x86_64-linux
1 gem installed

A new release of RubyGems is available: 3.4.10 → 3.4.22!
Run `gem update --system 3.4.22` to update your installation.

/ # ruby -rsqlite3 -e 'puts SQLite3::SQLITE_LOADED_VERSION'
4.44.2
/ # 

And here's the same on 3.19:

$ docker run --rm -it ruby:3.2.2-alpine3.19 /bin/sh
/ # gem install sqlite3
Fetching sqlite3-1.6.9-x86_64-linux.gem
Successfully installed sqlite3-1.6.9-x86_64-linux
1 gem installed

A new release of RubyGems is available: 3.4.10 → 3.4.22!
Run `gem update --system 3.4.22` to update your installation.

/ # ruby -rsqlite3 -e 'puts SQLite3::SQLITE_LOADED_VERSION'
<internal:/usr/local/lib/ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:85:in `require': cannot load such file -- sqlite3/sqlite3_native (LoadError)
	from <internal:/usr/local/lib/ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:85:in `require'
	from /usr/local/bundle/gems/sqlite3-1.6.9-x86_64-linux/lib/sqlite3.rb:6:in `rescue in <top (required)>'
	from /usr/local/bundle/gems/sqlite3-1.6.9-x86_64-linux/lib/sqlite3.rb:2:in `<top (required)>'
	from <internal:/usr/local/lib/ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:159:in `require'
	from <internal:/usr/local/lib/ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:159:in `rescue in require'
	from <internal:/usr/local/lib/ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:39:in `require'
<internal:/usr/local/lib/ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:85:in `require': Error relocating /usr/local/bundle/gems/sqlite3-1.6.9-x86_64-linux/lib/sqlite3/3.2/sqlite3_native.so: posix_fallocate64: symbol not found - /usr/local/bundle/gems/sqlite3-1.6.9-x86_64-linux/lib/sqlite3/3.2/sqlite3_native.so (LoadError)
	from <internal:/usr/local/lib/ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:85:in `require'
	from /usr/local/bundle/gems/sqlite3-1.6.9-x86_64-linux/lib/sqlite3.rb:4:in `<top (required)>'
	from <internal:/usr/local/lib/ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:159:in `require'
	from <internal:/usr/local/lib/ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:159:in `rescue in require'
	from <internal:/usr/local/lib/ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:39:in `require'
<internal:/usr/local/lib/ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:85:in `require': cannot load such file -- sqlite3 (LoadError)
	from <internal:/usr/local/lib/ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:85:in `require'
/ # 

I'm not sure if this is the gem or the docker image causing the issue here.

Similar report just came in #435, too.

I believe something changed in the alpine image, since we test alpine in the CI pipeline and it passed last week, but started failing yesterday with the new image

Will investigate. In the meantime, a workaround is to use ruby:3-alpine3.18

I copied /lib/ld-musl-x86_64.so.1 from ruby:3-alpine3.18 Docker image and that seems to fix the problem, so it would appear that the problem is related to some change in Musl libc or how Musl is compiled.

By looking the exported symbols from the library (e.g., readelf -Ws --dyn-syms /lib/ld-musl-x86_64.so.1 | grep posix_fallocate64) I've confirmed that the symbol posix_fallocate64 is present in the previous version but missing in the new version. I'm not sure yet if this was intended or if it was an oversight. I'll try to dig more.

Just a note: the symbol missing for the user in #435 is fcntl64, where the one reported missing in this issue is posix_fallocate64.

OK, it seems this was an intentional breaking change in Musl 1.24:

On the API level, the legacy "LFS64" ("large file support") interfaces, which were provided by macros remapping them to their standard names (#define stat64 stat and similar) have been deprecated and are no longer provided under the _GNU_SOURCE feature profile, only under explicit _LARGEFILE64_SOURCE. The latter will also be removed in a future version. Builds broken by this change can be fixed short-term by adding -D_LARGEFILE64_SOURCE to CFLAGS, but should be fixed to use the standard interfaces.

So if I understood correctly, functions with the 64 suffix were dropped and the correct fix is to use the unsuffixed versions instead.

Here are examples of other projects that were also affected by this:

@pdfrod Thank you so much for doing this research! I should have time tomorrow to investigate if I can work around this in the native (precompiled) gems.

I'm not sure there's a fix for this other than for rake-compiler-dock to support generating native musl-specific gems. Upstream issue is rake-compiler/rake-compiler-dock#75

Workarounds:

  • bundle config force_ruby_platform true
  • sed -i '/sqlite3/ s/$/, force_ruby_platform: true/' Gemfile

(2023-12-19 - Maintainer's note: these workarounds will compile the gem at installation time on the target machine, and are fully explained in INSTALLATION.md)

Thanks for looking into this; I did wonder if it was upstream.

I'll pin to alpine-3.18 for now.

I've updated the issue description with a TLDR summary of the workarounds.

Confirm a breaking change in the Alpine image. I redeployed the same app with no change to the Gemfile or Dockerfile and now receive this error:

[info] /usr/local/bundle/gems/sqlite3-1.7.0-x86_64-linux/lib/sqlite3.rb:6:in `require': cannot load such file -- sqlite3/sqlite3_native (LoadError)
[info] Did you mean? sqlite3/3.3/sqlite3_native
[info] sqlite3/3.2/sqlite3_native
[info] sqlite3/3.1/sqlite3_native
[info] sqlite3/3.0/sqlite3_native
[info] from /usr/local/bundle/gems/sqlite3-1.7.0-x86_64-linux/lib/sqlite3.rb:6:in `rescue in <top (required)>'
...

And that this diff got my deploy working again:

--- a/Dockerfile
+++ b/Dockerfile
@@ -1,4 +1,4 @@
-FROM ruby:3.2.2-alpine AS base
+FROM ruby:3.2.2-alpine3.18 AS base

( 👋 👋 Hi Mike)

Please follow #442 for the fix-in-progess for this issue.

Should be fixed in v2.0.0 with the linux-musl native gem.