Skip to content

Conversation

@marshallpierce
Copy link

pkg-config's default config will output rustc-link-lib when it finds a package. However, per this project's readme, linkage is the responsibility of the user, so it seems like merely probing for the version should not change any subsequent build config.

Lines previously present in build.rs output that are now gone:

[pcap 2.3.0] cargo:rustc-link-search=native=/usr/lib/x86_64-linux-gnu
[pcap 2.3.0] cargo:rustc-link-lib=pcap

Similarly, the #[link(name = "pcap")] in raw was making dependent binaries automatically link to libpcap.so.

Dependent crates will need to either use a #[link... snippet, or specify linkage via build.rs. That matches my understanding of the readme's linkage policy, and for what it's worth, as a user of the crate I'm fine with that. A downside with this approach is that it's possible to link libpcap twice since this crate doesn't specify link in its Cargo.toml, but presumably that's something you've already considered as a worthwhile tradeoff to avoid needing to have ever more environment variables to configure linkage.

@Wojtek242
Copy link
Collaborator

Hi, thanks for the PR!

However, I don't understand the problem but the solution to be honest.

Can I ask you to explain the original problem you are trying to solve? And how does your solution solve it?

@marshallpierce
Copy link
Author

Consider a simple program that uses pcap:

fn main() {
    println!("Device: {:?}", pcap::Device::lookup());
}

With pcap at f138c4b, its build.rs outputs:

[pcap 2.3.0] cargo:rustc-link-search=native=/usr/lib/x86_64-linux-gnu
[pcap 2.3.0] cargo:rustc-link-lib=pcap

And when compiling the pcap crate, we see -l pcap.

Looking at ldd for the final binary:

	linux-vdso.so.1 (0x00007f6149acf000)
	libpcap.so.0.8 => /lib/x86_64-linux-gnu/libpcap.so.0.8 (0x00007f61499fb000)
	libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f61499ca000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f61497d4000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f6149ad1000)
	libdbus-1.so.3 => /lib/x86_64-linux-gnu/libdbus-1.so.3 (0x00007f614977d000)
	libsystemd.so.0 => /lib/x86_64-linux-gnu/libsystemd.so.0 (0x00007f6149668000)
	libcap.so.2 => /lib/x86_64-linux-gnu/libcap.so.2 (0x00007f614965a000)
	libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f614956a000)

So, it's dynamically linking to libpcap.so automatically. Assuming you're happy with the current linking policy (which as a user I am), that's not the desired state. As a user, I would expect to see a linkage error rather than having it silently pick some way of linking when I fail to configure it correctly.

If we apply the build.rs change to have pkgconfig not emit cargo metadata, then we no longer see -l pcap in the compiler invocation for the pcap crate, but somehow through compiler magic™ we get the same linkage at the end:

	linux-vdso.so.1 (0x00007f94fda1e000)
	libpcap.so.0.8 => /lib/x86_64-linux-gnu/libpcap.so.0.8 (0x00007f94fd94a000)
	libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f94fd919000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f94fd723000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f94fda20000)
	libdbus-1.so.3 => /lib/x86_64-linux-gnu/libdbus-1.so.3 (0x00007f94fd6cc000)
	libsystemd.so.0 => /lib/x86_64-linux-gnu/libsystemd.so.0 (0x00007f94fd5b7000)
	libcap.so.2 => /lib/x86_64-linux-gnu/libcap.so.2 (0x00007f94fd5a9000)
	libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f94fd4b9000)

If we then remove the #[link(name = "pcap")] from raw.rs, we finally get the (to me) expected result of the compiler complaining when compiling the binary.

  = note: rust-lld: error: undefined symbol: pcap_freealldevs
          >>> referenced by device.rs:184 (src/device.rs:184)
          >>>               pcap-a357e56246ddafdc.74ee0048bqhyjppthqhj7etwq.1yct1bg.rcgu.o:(pcap::device::Device::with_all_devs::he29f295a83daa62d) in archive .../target/debug/deps/libpcap-a357e56246ddafdc.rlib

In my case, I want to statically link libpcap, so I compile libpcap.a the way I want, put it in /prebuilts (or wherever), and point to it from build.rs in my project. In this case I also need to link to dbus, but YMMV based on how libpcap is built:

use std::env;
use std::path::Path;

fn main() {
    let crate_dir = env::var_os("CARGO_MANIFEST_DIR").unwrap();
    let prebuilts_dir = Path::new(&crate_dir).join("prebuilts");
    println!("cargo:rustc-link-search={}", prebuilts_dir.display());
    println!("cargo:rustc-link-lib=static=pcap");
    println!("cargo:rustc-link-lib=dbus-1");
}

This produces:

	linux-vdso.so.1 (0x00007fd5ee624000)
	libdbus-1.so.3 => /lib/x86_64-linux-gnu/libdbus-1.so.3 (0x00007fd5ee502000)
	libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007fd5ee4d1000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fd5ee2db000)
	/lib64/ld-linux-x86-64.so.2 (0x00007fd5ee626000)
	libsystemd.so.0 => /lib/x86_64-linux-gnu/libsystemd.so.0 (0x00007fd5ee1c6000)
	libcap.so.2 => /lib/x86_64-linux-gnu/libcap.so.2 (0x00007fd5ee1ba000)
	libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fd5ee0c8000)

No dynamic libpcap, just as I wanted.

pkg-config's default config will output `rustc-link-lib` when it finds a
package. However, per this project's readme, linkage is the
responsibility of the user, so it seems like merely probing for the
version should not change any subsequent build config.

Lines previously present in build.rs output that are now gone:

```
[pcap 2.3.0] cargo:rustc-link-search=native=/usr/lib/x86_64-linux-gnu
[pcap 2.3.0] cargo:rustc-link-lib=pcap
```

Similarly, the `#[link(name = "pcap")]` in `raw` was making dependent
binaries automatically link to libpcap.so.
@Wojtek242
Copy link
Collaborator

Thanks for your very detailed explanation! I now understand the problem. Basically, it seems that if libpcap.so is present then it will dynamically link the library instead of the static one. I appreciate that this is a problem, but it is intentional that the build works as is if libpcap is present so I would rather not remove this "hands-off" dynamic linking if libpcap.so is present.

At the same time, it is important that you can link statically even if libpcap is present. I did look at this in the past and I think it's doable without any changes to the code base. See my comment here: #195 (comment).

Could you try the following flags in your project?:

        println!("cargo:rustc-link-lib=static=pcap");
        println!("cargo:rustc-link-lib=dylib=dbus-1");

and let me know if it works for you as expected?

@Wojtek242
Copy link
Collaborator

Actually, I just noticed that you already have those flags - is it the case that even with those flags, the dynamic library is linked?

@marshallpierce
Copy link
Author

It will statically link with rustc-link-lib=static=... in build.rs, so it is possible to achieve what I need with the existing setup, but I'd rather have it fail explicitly if I don't configure things rather than silently do something undesirable.

The behavior you describe is also a pretty direct contradiction of the readme:

It is your responsibility, as the crate user, to configure linking with libpcap/wpcap to suit your needs (e.g. library version, static vs. dynamic linking, etc.) via your own build script.

If you want to maintain the automatic dynamic linking, it would be great to see the readme updated to reflect that, and ideally an env var I can set to turn it off.

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