Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fat binary gem for Linux #551

Merged
merged 3 commits into from
Nov 28, 2024
Merged

fat binary gem for Linux #551

merged 3 commits into from
Nov 28, 2024

Conversation

larskanis
Copy link
Collaborator

@larskanis larskanis commented Oct 30, 2023

With the binary linux gem it's no longer necessary to install PostgreSQL client library or header files (like package libpq-dev).

Solves #480

@larskanis larskanis force-pushed the binary-linux branch 8 times, most recently from f69a294 to 97fe9b0 Compare November 3, 2023 10:10
@larskanis larskanis force-pushed the binary-linux branch 2 times, most recently from 49e89b9 to fb30680 Compare November 24, 2023 11:52
@larskanis larskanis force-pushed the master branch 3 times, most recently from 2b7e4fe to daec80f Compare February 28, 2024 14:01
@larskanis larskanis force-pushed the binary-linux branch 2 times, most recently from 2877b2c to f558d96 Compare April 8, 2024 18:47
@larskanis
Copy link
Collaborator Author

larskanis commented Apr 8, 2024

This PR currently fails with a segfault on my local machine when connecting by a TLS encrypted connection like so:

$ valgrind ruby -rpg -e "PG.connect host:'localhost'"
==513704== Invalid write of size 8
==513704==    at 0x2285E800: pgtls_close (in /home/lars/comcard/ruby-pg/ports/x86_64-linux-gnu/lib/libpq.so.5.16)
==513704==    by 0x2285799F: pqsecure_close (in /home/lars/comcard/ruby-pg/ports/x86_64-linux-gnu/lib/libpq.so.5.16)
==513704==    by 0x2283E7B2: pqDropConnection (in /home/lars/comcard/ruby-pg/ports/x86_64-linux-gnu/lib/libpq.so.5.16)
==513704==    by 0x228423E0: PQconnectPoll (in /home/lars/comcard/ruby-pg/ports/x86_64-linux-gnu/lib/libpq.so.5.16)
==513704==    by 0x2277A7EE: ??? (in /home/lars/.rvm/gems/ruby-3.3.0/gems/pg-1.5.4-x86_64-linux-gnu/lib/3.3/pg_ext.so)
==513704==    by 0x4AC5FB7: rb_nogvl (thread.c:1530)
==513704==    by 0x2277AD2D: gvl_PQconnectPoll (in /home/lars/.rvm/gems/ruby-3.3.0/gems/pg-1.5.4-x86_64-linux-gnu/lib/3.3/pg_ext.so)
==513704==    by 0x227833F5: ??? (in /home/lars/.rvm/gems/ruby-3.3.0/gems/pg-1.5.4-x86_64-linux-gnu/lib/3.3/pg_ext.so)
==513704==    by 0x4B18FC3: vm_call_cfunc_with_frame_ (vm_insnhelper.c:3490)
==513704==    by 0x4B1A3AA: vm_call_symbol (vm_insnhelper.c:3953)
==513704==    by 0x4AFD436: vm_sendish (vm_insnhelper.c:5581)
==513704==    by 0x4AFD436: vm_exec_core (insns.def:834)
==513704==    by 0x4AF9CDC: rb_vm_exec (vm.c:2486)
==513704==  Address 0x222d9f60 is 1,008 bytes inside a block of size 1,192 free'd
==513704==    at 0x484810F: free (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==513704==    by 0x22A86323: CRYPTO_free (in /home/lars/comcard/ruby-pg/ports/x86_64-linux-gnu/lib/libpq.so.5.16)
==513704==    by 0x22A121B8: sock_free (in /home/lars/comcard/ruby-pg/ports/x86_64-linux-gnu/lib/libpq.so.5.16)
==513704==    by 0x22A05F2F: BIO_free (in /home/lars/comcard/ruby-pg/ports/x86_64-linux-gnu/lib/libpq.so.5.16)
==513704==    by 0x22A0791F: BIO_free_all (in /home/lars/comcard/ruby-pg/ports/x86_64-linux-gnu/lib/libpq.so.5.16)
==513704==    by 0x2295A79D: ossl_ssl_connection_free (in /home/lars/comcard/ruby-pg/ports/x86_64-linux-gnu/lib/libpq.so.5.16)
==513704==    by 0x2295A249: SSL_free (in /home/lars/comcard/ruby-pg/ports/x86_64-linux-gnu/lib/libpq.so.5.16)
==513704==    by 0x2285E7FB: pgtls_close (in /home/lars/comcard/ruby-pg/ports/x86_64-linux-gnu/lib/libpq.so.5.16)
==513704==    by 0x2285799F: pqsecure_close (in /home/lars/comcard/ruby-pg/ports/x86_64-linux-gnu/lib/libpq.so.5.16)
==513704==    by 0x2283E7B2: pqDropConnection (in /home/lars/comcard/ruby-pg/ports/x86_64-linux-gnu/lib/libpq.so.5.16)
==513704==    by 0x228423E0: PQconnectPoll (in /home/lars/comcard/ruby-pg/ports/x86_64-linux-gnu/lib/libpq.so.5.16)
==513704==    by 0x2277A7EE: ??? (in /home/lars/.rvm/gems/ruby-3.3.0/gems/pg-1.5.4-x86_64-linux-gnu/lib/3.3/pg_ext.so)
==513704==  Block was alloc'd at
==513704==    at 0x4845828: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==513704==    by 0x228443E6: makeEmptyPGconn (in /home/lars/comcard/ruby-pg/ports/x86_64-linux-gnu/lib/libpq.so.5.16)
==513704==    by 0x2283EE00: PQconnectStart (in /home/lars/comcard/ruby-pg/ports/x86_64-linux-gnu/lib/libpq.so.5.16)
==513704==    by 0x2277A7CB: ??? (in /home/lars/.rvm/gems/ruby-3.3.0/gems/pg-1.5.4-x86_64-linux-gnu/lib/3.3/pg_ext.so)
==513704==    by 0x4AC5FB7: rb_nogvl (thread.c:1530)
==513704==    by 0x2277ACD5: gvl_PQconnectStart (in /home/lars/.rvm/gems/ruby-3.3.0/gems/pg-1.5.4-x86_64-linux-gnu/lib/3.3/pg_ext.so)
==513704==    by 0x22780B76: ??? (in /home/lars/.rvm/gems/ruby-3.3.0/gems/pg-1.5.4-x86_64-linux-gnu/lib/3.3/pg_ext.so)
==513704==    by 0x4B18FC3: vm_call_cfunc_with_frame_ (vm_insnhelper.c:3490)
==513704==    by 0x4AFD436: vm_sendish (vm_insnhelper.c:5581)
==513704==    by 0x4AFD436: vm_exec_core (insns.def:834)
==513704==    by 0x4AF9CDC: rb_vm_exec (vm.c:2486)
==513704==    by 0x4968A52: rb_ec_exec_node (eval.c:287)
==513704==    by 0x4968A52: ruby_run_node (???:328)
==513704==    by 0x109204: rb_main (main.c:39)
==513704==    by 0x109204: main (main.c:58)
==513704== 

@larskanis larskanis force-pushed the binary-linux branch 2 times, most recently from 0f5bbe6 to c77de0c Compare April 22, 2024 11:27
@larskanis larskanis force-pushed the binary-linux branch 2 times, most recently from a5f44a9 to 7e422cc Compare July 28, 2024 11:55
Rakefile Outdated Show resolved Hide resolved
- Rename rake task 'gem:windows' to 'gem:native'
- Add `rake gem:native:x86_64-linux`
- Replace own PostgreSQL and OpenSSL build tasks by MiniPortile
  This is a more standard way and allows easier extensions.
- Add krb5 library for Linux target to support GSSAPI/Kerberos
- Change loading of pg_ext
  Try lib/pg_ext in addition to lib/3.2/pg_ext to support `rake spec` in the build directory
- Fat binary linux gem: Try different UnixSocket paths of different distros.
- CI: Adjust binary tests for new cross build target
- Change patch directory to ports/patches/<package>/<version>/*.patch
- ostruct gem is no longer necessary
- Fix remaining "windows" references reg. fat binary gems
Fiddle will probably be removed from ruby stdlibs, so using a alternative approach seems necessary.
@larskanis larskanis force-pushed the binary-linux branch 9 times, most recently from 4b50d68 to fe4962e Compare November 27, 2024 18:08
@larskanis
Copy link
Collaborator Author

larskanis commented Nov 27, 2024

The OpenSSL issue above is resolved by #598. CI on Fedora and Alpine Linux is running and succeeds now.

The implementation is like so:

  • The cross build task builds krb5 and openssl as static libraries and link them to libpq.
  • It uses a dedicated shared object name: libpq-ruby-pg.so.1. This avoids mistakenly loaded system libpq files.
  • All supported ruby versions in the gem (2.7 to 3.3) link to one and the same shared object libpq-ruby-pg.so.1 to save space compared to static linking libpq into each extension file.
  • A $ORIGIN-relative rpath is the mechanism to locate and load the shared object file. It seems to be solid.
  • Only one binary gem with platform x86_64-linux is used, even for Musl based systems (Alpine Linux).
  • Only x86_64-linux is additional supported right now by this PR.
  • x86-mingw32, x64-mingw32 and x64-mingw-ucrt are now built per mini_portile2, but the result is the same as the previous Rakefile.cross.

The x86_64-linux gem runs on Alpine Linux, but requires the package gcompat there as long as we don't provide a native gem for platform x86_64-linux-musl. It is a very small package and it seems quite common, since nokogiri needs it too.

Providing a binary package adds the responsibility to update the gem in case of security updates to krb5, openssl or libpq. This was already the case for the Windows binary gems, but the new platform makes it more significant.

There is one use case the binary gem doesn't support: Retrieval of connection options from LDAP. To support this libldap would be necessary, but it has a lot of dependencies. I don't think it's a widely used feature and that it's worth to support it. If it's necessary the source gem can be forced.

So after more than one year I think this is ready to be merged.

To make sure, the new platform and the source gem is fetched by bundler, it can be added like so

bundle lock --add-platform x86_64-linux
bundle lock --add-platform ruby

to the Gemfile.lock and using a recent bundler version should do the trick.

Or if this doesn't work for some reason, forcing the usage of the source gem in the Gemfile:

gem "pg", force_ruby_platform: true

Or installing the source gem manually:

gem inst --platform ruby pg

@larskanis larskanis merged commit 53e7e6e into ged:master Nov 28, 2024
21 checks passed
@larskanis larskanis deleted the binary-linux branch November 28, 2024 09:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants