From 317c09a2196dd2f7e449efa8e595f2ce7e1ee0ee Mon Sep 17 00:00:00 2001 From: BarbossHack Date: Sat, 13 Dec 2025 17:05:32 +0100 Subject: [PATCH] Add symbol prefixing feature for BoringSSL --- .github/workflows/prefix.yml | 103 +++++++++++++++++++++++++++++++++++ boring-sys/Cargo.toml | 4 ++ boring-sys/build/config.rs | 3 + boring-sys/build/main.rs | 19 +++++++ boring-sys/build/prefix.rs | 89 ++++++++++++++++++++++++++++++ boring/Cargo.toml | 4 ++ 6 files changed, 222 insertions(+) create mode 100644 .github/workflows/prefix.yml create mode 100644 boring-sys/build/prefix.rs diff --git a/.github/workflows/prefix.yml b/.github/workflows/prefix.yml new file mode 100644 index 000000000..bf31f9326 --- /dev/null +++ b/.github/workflows/prefix.yml @@ -0,0 +1,103 @@ +name: prefix + +on: + pull_request: + branches: + - master + push: + branches: + - master + +jobs: + prefix-symbols: + runs-on: ${{ matrix.os }} + strategy: + matrix: + include: + - target: x86_64-unknown-linux-gnu + os: ubuntu-latest + bin: "" + test: true + custom_env: {} + - target: aarch64-unknown-linux-gnu + os: ubuntu-latest + bin: "" + test: false + apt_packages: crossbuild-essential-arm64 binutils-multiarch + custom_env: + CC_aarch64_unknown_linux_gnu: aarch64-linux-gnu-gcc + CXX_aarch64_unknown_linux_gnu: aarch64-linux-gnu-g++ + CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-g++ + - target: armv7-linux-androideabi + os: ubuntu-latest + bin: ndk + test: false + custom_env: {} + - target: aarch64-linux-android + os: ubuntu-latest + bin: ndk + test: false + custom_env: {} + - target: x86_64-linux-android + os: ubuntu-latest + bin: ndk + test: false + custom_env: {} + - target: i686-linux-android + os: ubuntu-latest + bin: ndk + test: false + custom_env: {} + defaults: + run: + working-directory: test-project + steps: + - name: Checkout repository + uses: actions/checkout@v5 + - name: Create test project + working-directory: . + run: | + cargo init test-project + cd test-project + cargo add boring-sys --path ../boring-sys/ + cargo add openssl-sys -F vendored + echo "fn main() {boring_sys::init(); openssl_sys::init();}" > src/main.rs + - name: Install Rust toolchain + shell: bash + run: rustup target add ${{ matrix.target }} + - name: Install target-specific APT dependencies + if: matrix.apt_packages != '' + run: sudo apt update && sudo apt install -y ${{ matrix.apt_packages }} + - name: Install cargo-ndk + if: contains(matrix.target, 'android') + run: | + cargo install cargo-ndk + # Openssl fix for i686: https://github.com/rust-openssl/rust-openssl/issues/2163#issuecomment-2692363343 + echo "ANDROID_NDK=$ANDROID_NDK_LATEST_HOME" >> $GITHUB_ENV + echo "ANDROID_NDK_HOME=$ANDROID_NDK_LATEST_HOME" >> $GITHUB_ENV + echo "ANDROID_NDK_ROOT=$ANDROID_NDK_LATEST_HOME" >> $GITHUB_ENV + echo "CARGO_TARGET_I686_LINUX_ANDROID_RUSTFLAGS=-C link-arg=-lclang_rt.builtins-i686-android -C link-arg=-L${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/lib/clang/21/lib/linux/" >> $GITHUB_ENV + - name: Build without prefixing + env: ${{ matrix.custom_env }} + shell: bash + run: | + RC=0 + cargo ${{ matrix.bin }} build --target ${{ matrix.target }} >logs 2>&1 || RC=$? + if [[ $RC == 0 || (! $(cat logs | grep "lld: error: duplicate symbol") && ! $(cat logs | grep "multiple definition of")) ]]; then + cat logs + echo "Build finished without duplicate symbols: $RC" + exit 1 + fi + cat logs + echo "Failed as expected: $RC" + - name: Add prefix-symbols feature + shell: bash + run: cargo add boring-sys --path ../boring-sys/ -F prefix-symbols + - name: Build with prefixing + env: ${{ matrix.custom_env }} + shell: bash + run: cargo ${{ matrix.bin }} build --target ${{ matrix.target }} + - name: Check if the is no runtime failures + if: matrix.test == true + shell: bash + run: cargo ${{ matrix.bin }} run --target ${{ matrix.target }} diff --git a/boring-sys/Cargo.toml b/boring-sys/Cargo.toml index ce49709d2..aebbbc6d9 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 or BoringSSL versions that might be linked 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..c294b5db4 100644 --- a/boring-sys/build/main.rs +++ b/boring-sys/build/main.rs @@ -1,3 +1,4 @@ +use core::panic; use fslock::LockFile; use std::env; use std::ffi::OsString; @@ -9,8 +10,10 @@ use std::process::{Command, Output}; use std::sync::OnceLock; use crate::config::Config; +use crate::prefix::{prefix_symbols, PrefixCallback}; mod config; +mod prefix; fn should_use_cmake_cross_compilation(config: &Config) -> bool { if config.host == config.target { @@ -543,6 +546,10 @@ fn built_boring_source_path(config: &Config) -> &PathBuf { .define("FIPS", "1"); } + if config.features.prefix_symbols { + cfg.define("CMAKE_POSITION_INDEPENDENT_CODE", "ON"); + } + cfg.build_target("ssl").build(); cfg.build_target("crypto").build() }) @@ -570,6 +577,14 @@ fn main() { if !config.env.docs_rs { emit_link_directives(&config); } + if config.features.prefix_symbols + && ["macos", "ios", "windows"].contains(&config.target_os.as_str()) + { + panic!("The `prefix_symbols` feature is not supported on macOS/iOS or windows targets."); + } + if config.features.prefix_symbols { + prefix_symbols(&config); + } generate_bindings(&config); } @@ -664,6 +679,10 @@ fn generate_bindings(config: &Config) { .clang_arg(sysroot.display().to_string()); } + if config.features.prefix_symbols { + builder = builder.parse_callbacks(Box::new(PrefixCallback)); + } + let headers = [ "aes.h", "asn1_mac.h", diff --git a/boring-sys/build/prefix.rs b/boring-sys/build/prefix.rs new file mode 100644 index 000000000..347daf121 --- /dev/null +++ b/boring-sys/build/prefix.rs @@ -0,0 +1,89 @@ +use crate::{config::Config, pick_best_android_ndk_toolchain, run_command}; +use std::{fs, io::Write, path::PathBuf, process::Command}; + +// The prefix to add to all symbols +// Using crate name to avoid collisions with other projects +const PREFIX: &str = env!("CARGO_CRATE_NAME"); + +// Callback to add a `link_name` macro with the prefix to all generated bindings +#[derive(Debug)] +pub struct PrefixCallback; + +impl bindgen::callbacks::ParseCallbacks for PrefixCallback { + fn generated_link_name_override( + &self, + item_info: bindgen::callbacks::ItemInfo<'_>, + ) -> Option { + Some(format!("{PREFIX}_{}", item_info.name)) + } +} + +fn android_toolchain(config: &Config) -> PathBuf { + let mut android_bin_path = config + .env + .android_ndk_home + .clone() + .expect("Please set ANDROID_NDK_HOME for Android build"); + android_bin_path.extend(["toolchains", "llvm", "prebuilt"]); + android_bin_path.push(pick_best_android_ndk_toolchain(&android_bin_path).unwrap()); + android_bin_path.push("bin"); + android_bin_path +} + +pub fn prefix_symbols(config: &Config) { + // List static libraries to prefix symbols in + let static_libs: Vec = [ + config.out_dir.join("build"), + config.out_dir.join("build").join("ssl"), + config.out_dir.join("build").join("crypto"), + ] + .iter() + .flat_map(|dir| { + ["libssl.a", "libcrypto.a"] + .into_iter() + .map(move |file| PathBuf::from(dir).join(file)) + }) + .filter(|p| p.exists()) + .collect(); + + // Use `nm` to list symbols in these static libraries + let nm = match &*config.target_os { + "android" => android_toolchain(config).join("llvm-nm"), + _ => PathBuf::from("nm"), + }; + let out = run_command(Command::new(nm).args(&static_libs)).unwrap(); + let mut redefine_syms: Vec = String::from_utf8_lossy(&out.stdout) + .lines() + .filter(|l| { + [" T ", " D ", " B ", " C ", " R ", " W "] + .iter() + .any(|s| l.contains(s)) + }) + .filter_map(|l| l.split_whitespace().nth(2).map(|s| s.to_string())) + .filter(|l| !l.starts_with("_")) + .map(|l| format!("{l} {PREFIX}_{l}")) + .collect(); + redefine_syms.sort(); + redefine_syms.dedup(); + + let redefine_syms_path = config.out_dir.join("redefine_syms.txt"); + let mut f = fs::File::create(&redefine_syms_path).unwrap(); + for sym in &redefine_syms { + writeln!(f, "{sym}").unwrap(); + } + f.flush().unwrap(); + + // Use `objcopy` to prefix symbols in these static libraries + let objcopy = match &*config.target_os { + "android" => android_toolchain(config).join("llvm-objcopy"), + _ => PathBuf::from("objcopy"), + }; + for static_lib in &static_libs { + run_command( + Command::new(&objcopy) + .arg(format!("--redefine-syms={}", redefine_syms_path.display())) + .arg(static_lib), + ) + .unwrap(); + } +} diff --git a/boring/Cargo.toml b/boring/Cargo.toml index d76c92945..2e750a200 100644 --- a/boring/Cargo.toml +++ b/boring/Cargo.toml @@ -39,6 +39,10 @@ rpk = ["boring-sys/rpk"] # `BORING_BSSL{,_FIPS}_ASSUME_PATCHED`. underscore-wildcards = ["boring-sys/underscore-wildcards"] +# Add a prefix to all symbols in libcrypto and libssl to prevent conflicts +# with other OpenSSL or BoringSSL versions that might be linked in the same process. +prefix-symbols = ["boring-sys/prefix-symbols"] + [dependencies] bitflags = { workspace = true } foreign-types = { workspace = true }