I’m building a library for FFI users that depends on a sibling library
which is also available through FFI. To illustrate the project layout:
.
├── dependency
│ ├── build.rs
│ ├── Cargo.toml
│ └── src
│ └── lib.rs
└── main
├── build.rs
├── Cargo.toml
└── src
└── lib.rs
Here both main
and dependency
are libraries of crate-type = ["lib", "cdylib"]
.
(In the actual product they’re part of a workspace but the problem
occurs without a workspace as well so let’s not complicate matters.)
main
depends on dependency
like so:
[dependencies]
dependency = { path = "../dependency" }
dependency
is a leaf library. Both main
and dependency
get
their DT_SONAME
embedded like so:
$ cat dependency/build.rs
fn main () { println!("cargo:rustc-cdylib-link-arg=-Wl,-soname,libdependency.so.0"); }
$ cat main/build.rs
fn main () { println!("cargo:rustc-cdylib-link-arg=-Wl,-soname,libmain.so.0"); }
(Actually I’m using the cdylib-link-lines
crate to accomplish this
but it boils down to the same issue.)
This works fine for libdependency.so
. However, when running
cargo run
in ./main/
, I obtain a libmain.so
with the
SONAME of libdependency.so
embedded:
$ readelf -d target/debug/libmain.so |grep SONAME
0x000000000000000e (SONAME) Library soname: [libdependency.so.0]
This is obviously wrong, it should be libmain.so.0
.. The reason
for this is that the rustc-cdylib-link-arg
lines get executed from
the build.rs
of both libraries. Thus both of them end up
getting appended to the rustc
command line:
rustc \
--crate-name main \
--edition=2021 src/lib.rs \
--error-format=json \
--json=diagnostic-rendered-ansi,artifacts,future-incompat \
--crate-type lib \
--crate-type cdylib \
--emit=dep-info,link \
-C embed-bitcode=no \
-C debuginfo=2 \
-C metadata=e0d0d2eeaf7bb2d0 \
--out-dir /home/phg/src/ugh-cargo/soname/main/target/debug/deps \
-C incremental=/home/phg/src/ugh-cargo/soname/main/target/debug/incremental \
-L dependency=/home/phg/src/ugh-cargo/soname/main/target/debug/deps \
--extern dependency=/home/phg/src/ugh-cargo/soname/main/target/debug/deps/libdependency.rlib \
-C link-arg=-Wl,-soname,libmain.so.0 \
-C link-arg=-Wl,-soname,libdependency.so.0
Now since there is only one SONAME field per object file, this
would actually work if the order of link-args
were reversed. As
it is, later arguments override earlier ones and bam, libmain.so
wrongly claims it’s libdependency.so.0
!
Ideally Cargo would support setting the SONAME directly in
Cargo.toml
.
Alternatively, I’d be happy with a mechanism to suppress the bad -C link-arg
in the above example when building libmain.so
. However,
conditionally disabling the println!
in dependency/build.rs
results in the SONAME being missing from libdependency.so
.
Is there a way to filter rustc
arguments added by dependencies?
I guess if I could tell cargo to put the output of main/build.rs
last, that would work too.