matsadler / magnus

Ruby bindings for Rust. Write Ruby extension gems in Rust, or call Ruby from Rust.
https://docs.rs/magnus/latest/magnus/
MIT License
659 stars 33 forks source link

Enabling the "embed" feature causes `undefined reference to 'uncompress'` on Linux #84

Open monorkin opened 1 year ago

monorkin commented 1 year ago

Hi, I was playing around with embedding Ruby into an app and ran into an issue on Linux where cargo run fails during compilation due to the following error:

  = note: /usr/bin/ld: /home/stanko/Desktop/crazy_idea/target/debug/deps/libmagnus-b135cde287fe83b2.rlib(addr2line.o): in function `uncompress_debug_section':
          /tmp/ruby-build.20230729185323.429701.n14x1w/ruby-3.2.1/addr2line.c:1978: undefined reference to `uncompress'
          collect2: error: ld returned 1 exit status

  = note: some `extern` functions couldn't be found; some native libraries may need to be installed or have their path specified
  = note: use the `-l` flag to specify native libraries to link
  = note: use the `cargo:rustc-link-lib` directive to specify the native libraries to link with Cargo (see https://doc.rust-lang.org/cargo/reference/build-scripts.html#cargorustc-link-libkindname)

error: could not compile `crazy_idea` (bin "crazy_idea") due to previous error
Full error message
    
   Compiling rb-sys v0.9.79
   Compiling magnus v0.4.4
   Compiling crazy_idea v0.1.0 (/home/stanko/Desktop/crazy_idea)
error: linking with `cc` failed: exit status: 1
  |
  = note: LC_ALL="C" PATH="/home/stanko/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/bin:/home/stanko/.asdf/shims:/home/stanko/.asdf/bin:/home/stanko/.zplug/bin:/home/stanko/Scripts:/home/stanko/.local/bin:/home/stanko/.cargo/bin:/home/stanko/.asdf/installs/nodejs/18.4.0/.npm/bin:/home/stanko/.yarn/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/home/stanko/.dotnet/tools:/var/lib/flatpak/exports/bin:/usr/lib/jvm/default/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:/usr/lib/rustup/bin" VSLANG="1033" "cc" "-m64" "/tmp/rustcK9plxZ/symbols.o" "/home/stanko/Desktop/crazy_idea/target/debug/deps/crazy_idea-5d69157a5b428023.1bcjakjgm9ldupmt.rcgu.o" "/home/stanko/Desktop/crazy_idea/target/debug/deps/crazy_idea-5d69157a5b428023.1ce1q2hdt5npiiyc.rcgu.o" "/home/stanko/Desktop/crazy_idea/target/debug/deps/crazy_idea-5d69157a5b428023.1i03mepp0gy0iltj.rcgu.o" "/home/stanko/Desktop/crazy_idea/target/debug/deps/crazy_idea-5d69157a5b428023.1ju3mga88h5jupje.rcgu.o" "/home/stanko/Desktop/crazy_idea/target/debug/deps/crazy_idea-5d69157a5b428023.29liimunk6ish5gv.rcgu.o" "/home/stanko/Desktop/crazy_idea/target/debug/deps/crazy_idea-5d69157a5b428023.2bi0bewo5sa2zjrt.rcgu.o" "/home/stanko/Desktop/crazy_idea/target/debug/deps/crazy_idea-5d69157a5b428023.2ln80v3wgultoh6j.rcgu.o" "/home/stanko/Desktop/crazy_idea/target/debug/deps/crazy_idea-5d69157a5b428023.2lwyyuw8vl6pdqws.rcgu.o" "/home/stanko/Desktop/crazy_idea/target/debug/deps/crazy_idea-5d69157a5b428023.2p583jesbt90zwcv.rcgu.o" "/home/stanko/Desktop/crazy_idea/target/debug/deps/crazy_idea-5d69157a5b428023.2qge5aqi25533tee.rcgu.o" "/home/stanko/Desktop/crazy_idea/target/debug/deps/crazy_idea-5d69157a5b428023.390ezfwlgzo4ds4r.rcgu.o" "/home/stanko/Desktop/crazy_idea/target/debug/deps/crazy_idea-5d69157a5b428023.3pqawr07p4cf0gaq.rcgu.o" "/home/stanko/Desktop/crazy_idea/target/debug/deps/crazy_idea-5d69157a5b428023.3qe12wxzqt1qf8sx.rcgu.o" "/home/stanko/Desktop/crazy_idea/target/debug/deps/crazy_idea-5d69157a5b428023.3wv1hogw6jgmjm2w.rcgu.o" "/home/stanko/Desktop/crazy_idea/target/debug/deps/crazy_idea-5d69157a5b428023.45rgg1bqeny9zvh8.rcgu.o" "/home/stanko/Desktop/crazy_idea/target/debug/deps/crazy_idea-5d69157a5b428023.4cko94khyf0bxo2s.rcgu.o" "/home/stanko/Desktop/crazy_idea/target/debug/deps/crazy_idea-5d69157a5b428023.4lveha4kch5lu9uw.rcgu.o" "/home/stanko/Desktop/crazy_idea/target/debug/deps/crazy_idea-5d69157a5b428023.4pc1q72spt0llgni.rcgu.o" "/home/stanko/Desktop/crazy_idea/target/debug/deps/crazy_idea-5d69157a5b428023.52x8n6xt5cbi8kds.rcgu.o" "/home/stanko/Desktop/crazy_idea/target/debug/deps/crazy_idea-5d69157a5b428023.555hd5bjj0w715p.rcgu.o" "/home/stanko/Desktop/crazy_idea/target/debug/deps/crazy_idea-5d69157a5b428023.5etkk4i84htpwo6d.rcgu.o" "/home/stanko/Desktop/crazy_idea/target/debug/deps/crazy_idea-5d69157a5b428023.cry3vs17reeb6gd.rcgu.o" "/home/stanko/Desktop/crazy_idea/target/debug/deps/crazy_idea-5d69157a5b428023.dmxt9z31a8tbkds.rcgu.o" "/home/stanko/Desktop/crazy_idea/target/debug/deps/crazy_idea-5d69157a5b428023.okzjqw64ie2u9lq.rcgu.o" "/home/stanko/Desktop/crazy_idea/target/debug/deps/crazy_idea-5d69157a5b428023.pcrw6dhh0ky82xq.rcgu.o" "/home/stanko/Desktop/crazy_idea/target/debug/deps/crazy_idea-5d69157a5b428023.pjhohbzrqmsywjf.rcgu.o" "/home/stanko/Desktop/crazy_idea/target/debug/deps/crazy_idea-5d69157a5b428023.to7gdbol6zayxh0.rcgu.o" "/home/stanko/Desktop/crazy_idea/target/debug/deps/crazy_idea-5d69157a5b428023.zh09phod34uiaxz.rcgu.o" "/home/stanko/Desktop/crazy_idea/target/debug/deps/crazy_idea-5d69157a5b428023.3tly0tfwqthht8y1.rcgu.o" "-Wl,--as-needed" "-L" "/home/stanko/Desktop/crazy_idea/target/debug/deps" "-L" "/home/stanko/Desktop/crazy_idea/ruby/lib" "-L" "/home/stanko/Desktop/crazy_idea/ruby/lib" "-L" "/home/stanko/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib" "-Wl,-Bstatic" "/home/stanko/Desktop/crazy_idea/target/debug/deps/libmagnus-b135cde287fe83b2.rlib" "/home/stanko/Desktop/crazy_idea/target/debug/deps/librb_sys-7c352b5a8d806963.rlib" "/home/stanko/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libstd-8389830094602f5a.rlib" "/home/stanko/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libpanic_unwind-41c1085b8c701d6f.rlib" "/home/stanko/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libobject-f733fcc57ce38b99.rlib" "/home/stanko/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libmemchr-6495ec9d4ce4f37d.rlib" "/home/stanko/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libaddr2line-1e3796360cca5b49.rlib" "/home/stanko/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libgimli-2e7f329b154436e1.rlib" "/home/stanko/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/librustc_demangle-1e1f5b8a84008aa8.rlib" "/home/stanko/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libstd_detect-cbcb223c64b13cf3.rlib" "/home/stanko/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libhashbrown-b40bc72e060a8196.rlib" "/home/stanko/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libminiz_oxide-1eb33ae9877d3c0f.rlib" "/home/stanko/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libadler-0335d894dd05bed7.rlib" "/home/stanko/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/librustc_std_workspace_alloc-076a893ead7e7ab5.rlib" "/home/stanko/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libunwind-2e924dd85b2e9d95.rlib" "/home/stanko/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libcfg_if-7975ffb5e62386c4.rlib" "/home/stanko/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/liblibc-285425b7cea12024.rlib" "/home/stanko/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/liballoc-38694d775e998991.rlib" "/home/stanko/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/librustc_std_workspace_core-914eb40be05d8663.rlib" "/home/stanko/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libcore-27094fcca7e14863.rlib" "/home/stanko/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libcompiler_builtins-919e055b306699ae.rlib" "-Wl,-Bdynamic" "-lrt" "-lgmp" "-ldl" "-lcrypt" "-lpthread" "-lrt" "-lgmp" "-ldl" "-lcrypt" "-lpthread" "-lgcc_s" "-lutil" "-lrt" "-lpthread" "-lm" "-ldl" "-lc" "-Wl,--eh-frame-hdr" "-Wl,-z,noexecstack" "-L" "/home/stanko/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib" "-o" "/home/stanko/Desktop/crazy_idea/target/debug/deps/crazy_idea-5d69157a5b428023" "-pie" "-Wl,-z,relro,-z,now" "-nodefaultlibs"
  = note: /usr/bin/ld: /home/stanko/Desktop/crazy_idea/target/debug/deps/libmagnus-b135cde287fe83b2.rlib(addr2line.o): in function `uncompress_debug_section':
          /tmp/ruby-build.20230729185323.429701.n14x1w/ruby-3.2.1/addr2line.c:1978: undefined reference to `uncompress'
          collect2: error: ld returned 1 exit status

  = note: some `extern` functions couldn't be found; some native libraries may need to be installed or have their path specified
  = note: use the `-l` flag to specify native libraries to link
  = note: use the `cargo:rustc-link-lib` directive to specify the native libraries to link with Cargo (see https://doc.rust-lang.org/cargo/reference/build-scripts.html#cargorustc-link-libkindname)

error: could not compile `crazy_idea` (bin "crazy_idea") due to previous error
    
  

When I try to compile and run the same code on a Mac it compiles and runs just fine. This error only pops up, for me, on Linux.

I'm unsure if this is user error on my part or a Linux specific issue. Any help is much appreciated.

What I've already tried

At first this seemed to be an issue with my Linux install, so I tried compiling and running my app inside the rb-sys dev Docker container and got the same error.

Dockerfile, build and run instructions
    
# This is a modified version of the rb-sys devcontainer Dockerfile
FROM mcr.microsoft.com/vscode/devcontainers/rust:bullseye

ENV CHRUBY_VERSION="0.3.9" \
  BUILD_LIST="build-essential clang libclang-dev openssh-server" \
  DEBIAN_FRONTEND="noninteractive" \
  RUBY_VERSIONS="3.1.2 3.2.2"

USER root

RUN set -ex; \
  apt-get update && \
  apt-get install -y --no-install-recommends $BUILD_LIST && \

  # postmodern gpg
  cd /tmp; \
  wget https://raw.github.com/postmodern/postmodern.github.io/main/postmodern.asc; \
  gpg --import postmodern.asc; \
  rm postmodern.asc; \

  # Install chruby
  mkdir /tmp/chruby-build; \
  cd /tmp/chruby-build; \
  wget -O chruby-$CHRUBY_VERSION.tar.gz https://github.com/postmodern/chruby/archive/v$CHRUBY_VERSION.tar.gz; \
  wget https://raw.github.com/postmodern/chruby/master/pkg/chruby-$CHRUBY_VERSION.tar.gz.asc; \
  gpg --verify chruby-$CHRUBY_VERSION.tar.gz.asc chruby-$CHRUBY_VERSION.tar.gz; \
  tar -xzvf chruby-$CHRUBY_VERSION.tar.gz; \
  cd chruby-$CHRUBY_VERSION/; \
  sudo bash -c "./scripts/setup.sh"; \
  echo "source /usr/local/share/chruby/chruby.sh" >> "/etc/profile.d/10-ruby.sh"; \
  echo "chruby 3.1" >> "/etc/profile.d/10-ruby.sh"; \
  cd /; \

  # Install ruby-build
  mkdir /tmp/ruby-build-build; \
  cd /tmp/ruby-build-build; \
  git clone https://github.com/rbenv/ruby-build.git; \
  PREFIX=/usr/local ./ruby-build/install.sh; \
  cd /; \

  # Install ruby versions
  echo "====== BUILDING RUBIES ========"; \
  echo 'gem: --no-document' >> $HOME/.gemrc; \
  for ver in $RUBY_VERSIONS; do \
  RUBY_CONFIGURE_OPTS="--disable-shared --disable-install-doc --disable-install-rdoc" ruby-build "$ver" "/opt/rubies/$ver"; \
  done; \

  # Cleanup
  apt-get clean && \
  rm -rf /tmp/* /var/lib/apt/lists/* /var/tmp/* /chruby* /usr/local/src/ruby*;
  
To build the container, and execute `cargo run` inside it I ran the following:
    
docker build -t crazy_idea .
docker run --rm -it -v $PWD:/app bash
cd app
RUBY=/opt/rubies/3.2.2/bin/ruby cargo run
    
  

Adding a .cargo/config.toml file to the project's dir and instructing rustc to keep unused symbols didn't help.

Contents of the config.toml file
    
[build]
# Without this flag, when linking static libruby, the linker removes symbols
# (such as `_rb_ext_ractor_safe`) which it thinks are dead code... but they are
# not, and they need to be included for the `embed` feature to work with static
# Ruby.
rustflags = ["-C", "link-dead-code=on"]
    
  

Different versions of magnus - from version 0.4 to 0.6 - all produce the same error.

Project

There is just one file - src/main.rs - that contains the embedding example from the docs

use magnus::{embed, eval};

fn main() {
    println!("Hello, Rust world!");

    let _cleanup = unsafe { embed::init() };

    let val: f64 = eval!("a + rand", a = 1).unwrap();

    println!("{}", val);
}

this is the project's cargo.toml file

[package]
name = "crazy_idea"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
magnus = { version = "0.4", features = ["ruby-static", "embed"] }

Ruby is built using RUBY_CONFIGURE_OPTS="--disable-shared --disable-install-doc --disable-install-rdoc" ruby-build "3.2.2" "$HOME/Desktop/crazy_idea/ruby"

The project is compiled and ran using RUBY=$PWD/ruby/bin/ruby cargo run

matsadler commented 1 year ago

I'm not really sure what's happing here.

The error is referencing this code in Ruby: https://github.com/ruby/ruby/blob/v3_2_2/addr2line.c#L1978 which references a function from zlib. This isn't directly used by Magnus, but I assume some part of Ruby that Magnus does reference eventually ends up referencing that.

It's likely it doesn't cause a problem on macOS because that code is inside an #ifdef that means that code won't exist on macOS.

I think what has happened is that the statically compiled Ruby isn't 100% static, and still has a reference to a dynamic library (zlib). Then when cargo is trying to link your program it can't find where that function is coming from.

This is hitting the edge of my knowledge about static compilation and linking, so I don't know enough to fix it or test if my theory is right.

monorkin commented 1 year ago

I experimented a little bit over the last 2 days and now have a different error :D

➜  crazy_idea (main) ✗ RUBY=$PWD/ruby/bin/ruby cargo run
   Compiling rb-sys v0.9.79
   Compiling magnus v0.4.4
   Compiling crazy_idea v0.1.0 (/home/stanko/Desktop/crazy_idea)
    Finished dev [unoptimized + debuginfo] target(s) in 5.34s
     Running `target/debug/crazy_idea`
Hello, Rust world!
target/debug/crazy_idea: symbol lookup error: /home/stanko/Desktop/crazy_idea/ruby/lib/ruby/3.2.0/x86_64-linux/enc/encdb.so: undefined symbol: rb_encdb_declare

Can you help me with this one too?

To get to this error I followed your hunch of this being a linking error to zlib caused by the ifdef in addr2line.c

At first I tried to disable compression of debug headers by adding --with-compress-debug-sections=no to RUBY_CONFIGURE_OPTS when compiling ruby using ruby-build. But this config option doesn't seem to have any effect.

Then I tried adding -l:libz.a to LDFLAGS, DLDFLAGS and CFLAGS when compiling ruby, but it also didn't help.

I tried passing--with-static-linked-ext=yes when compiling ruby, that also didn't help and even caused new errors due to missing .so files when I finally got the app to compile.

In the end I tried to dynamically link zlib by adding "-l", "z" to rustflags which worked, but I am now getting an undefined symbol: rb_encdb_declare error.

Once I figured out that linking to zlib from rust was a step forward I changed the linking to be static, which still produces the same error.

Here is the final .cargo/config.toml file:

[build]
# Without this flag, when linking static libruby, the linker removes symbols
# (such as `_rb_ext_ractor_safe`) which it thinks are dead code... but they are
# not, and they need to be included for the `embed` feature to work with static
# Ruby.
rustflags = ["-C", "link-dead-code=on", "-L", "/usr/lib/", "-l", "static=z"]

Ruby was built with this command

RUBY_CONFIGURE_OPTS="--disable-shared --disable-install-doc --disable-install-rdoc" ruby-build "3.2.1" "$HOME/Desktop/crazy_idea/ruby" --verbose