build: force -fno-lto for the bundled libopus build#21
Conversation
The bundled `libopus.a` is consumed by rustc (objects bundled into the
rlib via cargo's `+bundle` modifier), not by a C linker, so C-level LTO
gains nothing here. When ambient CFLAGS contain `-flto` (e.g. Arch's
`makepkg` with `OPTIONS=(lto)`, Gentoo `LTOFLAGS`, distros enabling it
by default), GCC produces "slim LTO" objects: empty `.text`, code only
in `.gnu.lto_*` sections, marked with `__gnu_lto_slim`. rust-lld then
links the rlib without an LTO plugin and reports `opus_decoder_create`,
`opus_decoder_destroy`, and `opus_decode` as undefined.
Repro on Arch with `makepkg`-set CFLAGS containing `-flto=auto`:
rust-lld: error: undefined symbol: opus_decoder_destroy
>>> referenced by symphonia_adapter_libopus...
Append `-fno-lto` to the cmake C flags so libopus is always built with
real native code regardless of inherited flags. gcc/clang honor the
last `-flto*` argument, so this overrides any earlier `-flto`, and is a
no-op for compilers that don't recognize it.
|
Thanks, I wasn't aware of it Can you please add |
|
@UMCEKO Nevermind, it does nothing when off |
|
#22 looks great — guarding on re: not reproducing on Fedora — fwiw Fedora's default %build_cflags doesn't include |
No, what I mean is that I set LDFLAGS and CFLAGS as yours, but I think it would be the best to make dedicated docker image with one of the systems where you encounter issues so that I can add workflow to test for this situation But for now, I would appreciate if you could test #22 so that I merge it and release new version of opusic-sys today to unblock you. |
|
Fixed in #22 |
Summary
When
opusic-sysis built with thebundledfeature (default) and theambient
CFLAGScontain-flto*— common on distros that enable LTO bydefault for package builds, e.g. Arch's
makepkgwithOPTIONS=(lto)appending
LTOFLAGS="-flto=auto", Gentoo'sLTOFLAGS, etc. — thefinal link of any binary that pulls
opusic-systransitively failswith:
even though
libopus.ais built and bundled intolibopusic_sys.rlib.Root cause
With
-fltoinCFLAGS, GCC produces slim LTO objects whencompiling the libopus sources:
.textis empty (size 0)..gnu.lto_*sections.__gnu_lto_slimmarker.rustc bundles those
.ofiles into the rlib (cargo's default+bundlemodifier on
cargo:rustc-link-lib=static=opus). When a downstreambinary links, rust-lld processes the rlib without an LTO plugin loaded,
sees no native code in
.text, and the symbols look undefined.readelf -SW opus_decoder.c.oon a failing build:The bundled
libopus.ais consumed by rustc, not a C linker, so doingLTO at the C level gains nothing in this build path — it only ever
breaks it.
Fix
Append
-fno-ltoto the cmake C flags. gcc/clang honor the last-flto*argument on the command line, so this overrides any inherited-flto. It is a no-op for compilers that don't recognize it.After the fix,
opus_decoder.c.ohas real native code in.text.opus_decoder_createand the link succeeds.Reproducer
On Arch (or any host with
-flto*inCFLAGS), build any crate thatlinks opusic-sys transitively (e.g.
symphonia-adapter-libopus):Without this patch:
rust-lld: error: undefined symbol: opus_decoder_*.With this patch: links cleanly. Verified locally against
kopuz(usessymphonia-adapter-libopus → opusic-sys) onrustc 1.95.0with rust-lld.Test plan
OPTIONS=(lto)before the patch (reproduced)OPTIONS=(lto)after the patch (verified end-to-end binary containsopus_decoder_create)readelfconfirms post-fix.ofiles are normal ELF, no.gnu.lto_*sections, no__gnu_lto_slimmarkertests/version.rsshould still pass (compiler had no-fltoin its baseline CFLAGS, so no behavioral change expected)