sgrif / pq-sys

Auto-generated rust bindings for libpq
Apache License 2.0
41 stars 32 forks source link

build does not set RPATH #36

Open davepacheco opened 3 years ago

davepacheco commented 3 years ago

The binaries generated by cargo don't set RPATH, which means they don't work unless you happen to have libpq installed in a location that's on the runtime linker's default search path. That happens to be true for lots of people but definitely isn't everyone.

To give you an example, in my case, I've got pg_config and the libraries are here:

$ which pg_config
/opt/ooce/bin/pg_config
$ pg_config --libdir
/opt/ooce/pgsql-13/lib/amd64

If I run cargo test from a clean checkout (looking at 3e367d53019a2740054d5dc6946e07931f1fb70b, the tip of master right now), it builds okay but fails at runtime:

$ cargo test
    Updating crates.io index
   Compiling pq-sys v0.4.6 (/home/dap/pq-sys)
    Finished test [unoptimized + debuginfo] target(s) in 6.45s
     Running unittests (target/debug/deps/pq_sys-f0d6d12067370f8b)

running 8 tests
test bindgen_test_layout__PQconninfoOption ... ok
test bindgen_test_layout__PQprintOpt ... ok
test bindgen_test_layout___sFILE ... ok
test bindgen_test_layout__bindgen_ty_8 ... ok
test bindgen_test_layout___sbuf ... ok
test bindgen_test_layout_pgNotify ... ok
test bindgen_test_layout_pgresAttDesc ... ok
test bindgen_test_layout__bindgen_ty_8__bindgen_ty_1 ... ok

test result: ok. 8 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

     Running tests/smoke.rs (target/debug/deps/smoke-b3d1fb683ab2f0c8)
ld.so.1: smoke-b3d1fb683ab2f0c8: fatal: libpq.so.5: open failed: No such file or directory
error: test failed, to rerun pass '--test smoke'

Caused by:
  process didn't exit successfully: `/home/dap/pq-sys/target/debug/deps/smoke-b3d1fb683ab2f0c8` (signal: 9, SIGKILL: kill)
$

The reason is that the built binary depends on libpq (which is correct) but the RPATH does not include /opt/ooce/pgsql-13.

$ readelf -d /home/dap/pq-sys/target/debug/deps/smoke-b3d1fb683ab2f0c8

Dynamic section at offset 0x1d95b8 contains 42 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libpq.so.5]
 0x0000000000000001 (NEEDED)             Shared library: [libsocket.so.1]
 0x0000000000000001 (NEEDED)             Shared library: [librt.so.1]
 0x0000000000000001 (NEEDED)             Shared library: [libpthread.so.1]
 0x0000000000000001 (NEEDED)             Shared library: [libumem.so.1]
 0x0000000000000001 (NEEDED)             Shared library: [libgcc_s.so.1]
 0x0000000000000001 (NEEDED)             Shared library: [libm.so.2]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.1]
 0x000000000000000c (INIT)               0x5a7410
 0x000000000000000d (FINI)               0x5a7420
 0x000000000000001d (RUNPATH)            Library runpath: [/usr/gcc/10/lib/amd64]
 0x000000000000000f (RPATH)              Library rpath: [/usr/gcc/10/lib/amd64]
...

Cargo does not have great support for setting the RPATH -- see rust-lang/cargo#5077. I can work around this on my system using the cargo:rustc-link-arg instruction to pass -Wl,-R/the/library/path to the compiler driver at build time. I will test this out on a few systems and send a PR shortly!

davepacheco commented 3 years ago

For what it's worth, this is still a problem, but I don't think there's a great way to solve it from pq-sys. The problem with the suggestion I had above (implemented in #37) is that these linker args aren't passed up to the builds of dependencies. So while this causes cargo test to work on the pq-sys repo, programs that use pq-sys still can't find the library.

I experimented with a different approach where pq-sys would emit Cargo metadata saying where it found the library. You can see this in this branch. Then, in the crate that uses pq-sys, I have a build script that looks like this:

fn main() {
    if let Some(rpaths) = std::env::var_os("DEP_PQ_LIBDIRS") {
        for p in std::env::split_paths(&rpaths) {
            println!("cargo:rustc-link-arg=-Wl,-R{}", p.to_string_lossy());
        }
    }
}

This works except for two caveats: first, that metadata is only available to immediate dependents. In my case, my crate depends on diesel, which depends on pq-sys. So I had to add a dummy dependency from my crate on pq-sys just so that my build script could get this metadata. This is only correct because Cargo will dedup them and only build pq-sys once. The second caveat is that it doesn't work for doctests. I hope this is just an oversight.

Ultimately, I decided to try linking statically instead. I ran into other problems with this that I'll mention in #27.