diff --git a/boring-sys/Cargo.toml b/boring-sys/Cargo.toml index ce49709d2..5f739b334 100644 --- a/boring-sys/Cargo.toml +++ b/boring-sys/Cargo.toml @@ -64,6 +64,10 @@ rpk = [] # `BORING_BSSL{,_FIPS}_SOURCE_PATH`. underscore-wildcards = [] +# Add a prefix to all symbols in libcrypto and libssl to prevent conflicts +# with other OpenSSL/BoringSSL versions in the same process. +prefix-symbols = [] + [build-dependencies] bindgen = { workspace = true } cmake = { workspace = true } diff --git a/boring-sys/build/config.rs b/boring-sys/build/config.rs index 25aaabf40..a8aaed629 100644 --- a/boring-sys/build/config.rs +++ b/boring-sys/build/config.rs @@ -18,6 +18,7 @@ pub(crate) struct Features { pub(crate) fips: bool, pub(crate) rpk: bool, pub(crate) underscore_wildcards: bool, + pub(crate) prefix_symbols: bool, } pub(crate) struct Env { @@ -105,11 +106,13 @@ impl Features { let fips = env::var_os("CARGO_FEATURE_FIPS").is_some(); let rpk = env::var_os("CARGO_FEATURE_RPK").is_some(); let underscore_wildcards = env::var_os("CARGO_FEATURE_UNDERSCORE_WILDCARDS").is_some(); + let prefix_symbols = env::var_os("CARGO_FEATURE_PREFIX_SYMBOLS").is_some(); Self { fips, rpk, underscore_wildcards, + prefix_symbols, } } diff --git a/boring-sys/build/main.rs b/boring-sys/build/main.rs index 41789cee3..2cd64aca1 100644 --- a/boring-sys/build/main.rs +++ b/boring-sys/build/main.rs @@ -9,8 +9,10 @@ use std::process::{Command, Output}; use std::sync::OnceLock; use crate::config::Config; +use crate::prefix::{apply_symbol_prefixes, SymbolPrefixCallbacks}; mod config; +mod prefix; fn should_use_cmake_cross_compilation(config: &Config) -> bool { if config.host == config.target { @@ -543,6 +545,13 @@ fn built_boring_source_path(config: &Config) -> &PathBuf { .define("FIPS", "1"); } + // We must enable Position Independent Code to ensure that the + // resulting prefixed symbols remain compatible with the relocatable + // nature of the final Rust test binaries to prevent linker errors + if config.features.prefix_symbols { + cfg.define("CMAKE_POSITION_INDEPENDENT_CODE", "ON"); + } + cfg.build_target("ssl").build(); cfg.build_target("crypto").build() }) @@ -570,6 +579,9 @@ fn main() { if !config.env.docs_rs { emit_link_directives(&config); } + if config.features.prefix_symbols { + apply_symbol_prefixes(&config); + } generate_bindings(&config); } @@ -658,6 +670,10 @@ fn generate_bindings(config: &Config) { .clang_arg("-I") .clang_arg(include_path.display().to_string()); + if config.features.prefix_symbols { + builder = builder.parse_callbacks(Box::new(SymbolPrefixCallbacks)); + } + if let Some(sysroot) = &config.env.sysroot { builder = builder .clang_arg("--sysroot") diff --git a/boring-sys/build/prefix.rs b/boring-sys/build/prefix.rs new file mode 100644 index 000000000..4fcfb3df2 --- /dev/null +++ b/boring-sys/build/prefix.rs @@ -0,0 +1,98 @@ +use crate::{config::Config, run_command}; +use std::{fs, io::Write, path::PathBuf, process::Command}; + +/// Prefix applied to all BoringSSL symbols so they don't collide with OpenSSL. +/// Uses the crate version to generate a dynamic prefix. +const SYMBOL_PREFIX: &str = concat!( + "BSSL_", + env!("CARGO_PKG_VERSION_MAJOR"), + "_", + env!("CARGO_PKG_VERSION_MINOR"), + "_", + env!("CARGO_PKG_VERSION_PATCH"), +); + +/// Bindgen callback that rewrites link names to use the prefixed symbol. +/// +/// C symbol `SSL_new` → Rust binding `#[link_name = "BSSL_SSL_new"]`. +#[derive(Debug)] +pub struct SymbolPrefixCallbacks; + +impl bindgen::callbacks::ParseCallbacks for SymbolPrefixCallbacks { + fn generated_link_name_override( + &self, + item_info: bindgen::callbacks::ItemInfo<'_>, + ) -> Option { + Some(format!("{SYMBOL_PREFIX}_{}", item_info.name)) + } +} + +/// Rewrite all global symbols in libssl.a/libcrypto.a to use the S2N_BSSL_ prefix. +/// +/// This runs `nm` to list symbols and `objcopy --redefine-syms` to edit the archives +/// in-place, so they can safely coexist with other {libssl,libcrypto} in the process. +pub fn apply_symbol_prefixes(config: &Config) { + // CMake output directories where libssl.a/libcrypto.a are expected. + let static_lib_dirs = [ + config.out_dir.join("build"), + config.out_dir.join("build").join("ssl"), + config.out_dir.join("build").join("crypto"), + ]; + + let static_libs: Vec = static_lib_dirs + .iter() + .flat_map(|dir| { + ["libssl.a", "libcrypto.a"] + .into_iter() + .map(move |file| dir.join(file)) + }) + .filter(|path| path.exists()) + .collect(); + + if static_libs.is_empty() { + eprintln!("warning: no libssl.a/libcrypto.a archives found to prefix"); + return; + } + + // 1. Use `nm` to list global symbols in the archives. + let nm_output = run_command(Command::new("nm").args(&static_libs)) + .expect("failed to run `nm` on BoringSSL archives"); + + let mut mappings: Vec = String::from_utf8_lossy(&nm_output.stdout) + .lines() + // Keep only global symbol types we care about. + .filter(|line| { + [" T ", " D ", " B ", " C ", " R ", " W "] + .iter() + .any(|marker| line.contains(marker)) + }) + // Symbol name is usually the 3rd column. + .filter_map(|line| line.split_whitespace().nth(2).map(str::to_owned)) + // Skip leading-underscore internals. + .filter(|sym| !sym.starts_with('_')) + // Compose `old new` mapping line: `sym S2N_BSSL_sym`. + .map(|sym| format!("{sym} {SYMBOL_PREFIX}_{sym}")) + .collect(); + + mappings.sort(); + mappings.dedup(); + + let mapping_file = config.out_dir.join("redefine_syms.txt"); + let mut f = fs::File::create(&mapping_file) + .expect("failed to create redefine_syms.txt for symbol prefixing"); + + for mapping in &mappings { + writeln!(f, "{mapping}").expect("failed to write symbol mapping"); + } + f.flush().expect("failed to flush symbol mapping file"); + + // 2. Use `objcopy` to apply the mapping to each archive in-place. + for static_lib in &static_libs { + run_command( + Command::new("objcopy") + .arg(format!("--redefine-syms={}", mapping_file.display())) + .arg(static_lib), + ) + .expect("failed to run `objcopy` to redefine symbols"); + } +} diff --git a/boring/Cargo.toml b/boring/Cargo.toml index d76c92945..76081955a 100644 --- a/boring/Cargo.toml +++ b/boring/Cargo.toml @@ -38,6 +38,8 @@ rpk = ["boring-sys/rpk"] # feature set can be provided by setting `BORING_BSSL{,_FIPS}_SOURCE_PATH` and # `BORING_BSSL{,_FIPS}_ASSUME_PATCHED`. underscore-wildcards = ["boring-sys/underscore-wildcards"] +# Enable symbol prefixing in the underlying BoringSSL build (for s2n tests). +prefix-symbols = ["boring-sys/prefix-symbols"] [dependencies] bitflags = { workspace = true }