2026-08-30
Stack Overflow: View Question
Tags: rust, cryptography, linker, rusqlite, advice
Score: 0 | Views: 110
The asker has a Rust desktop app that pulls in two dependencies that each statically bundle their own crypto library: rusqlite with bundled-sqlcipher (built against OpenSSL) and livekit via webrtc-sys (bundling BoringSSL). Because BoringSSL is an API-compatible fork of OpenSSL, both objects export overlapping symbol names — SSL_CTX_new, EVP_*, RSA_*, etc. — with subtly different ABIs and struct layouts. At link time you either get "multiple definition" errors or, worse, one wins and the other crate silently calls into the wrong implementation, corrupting state or crashing on first use.
Why it's hard: This isn't a Rust-level problem — it's a C symbol collision that Cargo can't see. Rust's crate namespacing doesn't help once you cross the FFI boundary. Both static archives dump their symbols into the final binary's global namespace, and ELF/Mach-O/PE all resolve by name. The ABIs look identical but aren't (BoringSSL removed things, changed struct layouts, and its SSL_CTX is smaller than OpenSSL's). A pointer allocated by one and freed by the other is a heap corruption waiting to happen.
Approaches, roughly in order of pragmatism:
rusqlite's bundled-sqlcipher-vendored-openssl exists precisely for this, but you can also disable bundled-sqlcipher and link against a system OpenSSL — or against the BoringSSL that webrtc already ships, since SQLCipher's crypto needs are a small subset that BoringSSL satisfies. Rebuilding SQLCipher against BoringSSL is annoying but eliminates the conflict entirely.cdylib. Wrap one of the offenders (say webrtc) in its own cdylib built with -Wl,--exclude-libs,ALL on Linux or -fvisibility=hidden equivalents on macOS/Windows. The crypto symbols become local to the shared object and don't leak into the outer link. This is what many Google projects do to isolate BoringSSL.objcopy --redefine-syms over one archive to prefix its symbols (bssl_SSL_CTX_new). Then patch the calling crate's bindings. Brittle and version-sensitive, but works when upstream won't budge.Gotchas: even after you "fix" the link, watch for TLS init clashes (both libraries may install signal handlers or global engines), FIPS-mode assumptions, and the fact that OpenSSL and BoringSSL disagree on error-queue semantics. On macOS the two-level namespace hides some of this until you ship; on Linux with default flat namespace, expect louder failures. And test in release mode — LTO can reshuffle which duplicate "wins."
