From df6296222da05b0cf6e3b16d3ec52869462ccea9 Mon Sep 17 00:00:00 2001 From: Geoff Martin Date: Thu, 10 Oct 2024 15:39:09 +0100 Subject: [PATCH 1/6] Initial attempt at making C library symbols unique for the 0.2.x version of Cyclors. --- build.rs | 116 ++++++++++++++++++++++++++++++++++++++++++++++++++++- src/lib.rs | 6 +++ 2 files changed, 121 insertions(+), 1 deletion(-) diff --git a/build.rs b/build.rs index e5dfcae..fa38278 100644 --- a/build.rs +++ b/build.rs @@ -1,7 +1,11 @@ extern crate bindgen; +use std::collections::HashSet; use std::env; -use std::path::PathBuf; +use std::fs::File; +use std::io::{LineWriter, Write}; +use std::path::{Path, PathBuf}; +use std::process::Command; fn main() { let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); @@ -139,6 +143,56 @@ fn main() { .clang_arg(format!("-I{}", cyclocut_include.to_str().unwrap())) .generate_comments(false); + // Prefix symbols in Iceoryx, Cyclone DDS and Cyclocut libraries to ensure uniqueness + #[cfg(target_os = "linux")] + { + // Prefix = cyclors__ + let mut prefix = env::var("CARGO_PKG_VERSION").unwrap().replace(".", "_"); + prefix.insert_str(0, "cyclors_"); + prefix.push('_'); + println!("Library prefix: {}", prefix); + + let mut symbols = HashSet::new(); + + let cyclone_symbols = get_defined_symbols(&cyclonedds_lib, "libddsc.a") + .expect("Failed to get symbols from libddsc.a!"); + symbols.extend(cyclone_symbols); + prefix_symbols(&cyclonedds_lib, "libddsc.a", &prefix, &symbols); + + let cyclocut_symbols = get_defined_symbols(&cyclocut_lib, "libcdds-util.a") + .expect("Failed to get symbols from libcdds-util.a!"); + symbols.extend(cyclocut_symbols); + prefix_symbols(&cyclocut_lib, "libcdds-util.a", &prefix, &symbols); + + #[derive(Debug)] + struct PrefixLinkNameCallback { + prefix: String, + symbols: HashSet, + } + + impl bindgen::callbacks::ParseCallbacks for PrefixLinkNameCallback { + fn generated_link_name_override( + &self, + item_info: bindgen::callbacks::ItemInfo<'_>, + ) -> Option { + match self.symbols.contains(item_info.name) { + true => { + let mut prefix = self.prefix.clone(); + prefix.push_str(item_info.name); + //println!("bindgen: {prefix}"); + Some(prefix) + } + false => None, + } + } + } + + bindings = bindings.parse_callbacks(Box::new(PrefixLinkNameCallback { + prefix: prefix.clone(), + symbols: symbols.clone(), + })); + } + // Add *IMAGE_TLS_DIRECTORY* to blocklist on Windows due to // https://github.com/rust-lang/rust-bindgen/issues/2179 #[cfg(target_os = "windows")] @@ -153,3 +207,63 @@ fn main() { .write_to_file(out_dir.join("bindings.rs")) .expect("Couldn't write bindings!"); } + +fn get_defined_symbols(lib_dir: &Path, lib_name: &str) -> Result, String> { + let lib_path = lib_dir.to_path_buf().join(lib_name); + println!( + "Getting defined symbols from {}", + lib_path.to_str().unwrap() + ); + let output = Command::new("nm") + .arg("-U") + .arg("-A") + .arg(lib_path) + .output() + .expect("Failed to run nm"); + + let stdout = String::from_utf8_lossy(&output.stdout); + let stderr = String::from_utf8_lossy(&output.stderr); + + match stderr.is_empty() { + true => { + let mut result: HashSet = HashSet::new(); + for line in stdout.lines() { + let tokens: Vec<&str> = line.split_whitespace().collect(); + let symbol = *tokens.last().unwrap(); + result.insert(String::from(symbol)); + } + Ok(result) + } + false => Err(String::from(stderr)), + } +} + +fn prefix_symbols(lib_dir: &Path, lib_name: &str, prefix: &str, symbols: &HashSet) { + let mut objcopy_file_name = lib_name.to_owned(); + objcopy_file_name.push_str(".objcopy"); + + let lib_file_path = lib_dir.to_path_buf().join(lib_name); + let symbol_file_path = lib_dir.to_path_buf().join(objcopy_file_name); + let symbol_file = File::create(symbol_file_path.clone()).expect("Failed to create symbol file"); + let mut symbol_file = LineWriter::new(symbol_file); + + for symbol in symbols { + let mut symbol_arg = symbol.clone(); + symbol_arg.push(' '); + symbol_arg.push_str(prefix); + symbol_arg.push_str(symbol); + symbol_arg.push('\n'); + symbol_file + .write_all(symbol_arg.as_bytes()) + .expect("Failed to write symbol file"); + } + symbol_file.flush().expect("Failed to flush symbol file"); + + let arg = format!("--redefine-syms={}", symbol_file_path.to_str().unwrap()); + + Command::new("objcopy") + .arg(arg) + .arg(lib_file_path) + .output() + .expect("Failed to run objcopy"); +} diff --git a/src/lib.rs b/src/lib.rs index 998d894..dd70615 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -50,9 +50,11 @@ pub use bindings::*; /* Additional wrapper functions for select exported inline functions */ extern "C" { + #[link_name = "cyclors_0_2_1_ddsi_serdata_size"] pub fn ddsi_serdata_size(d: *const ddsi_serdata) -> u32; } extern "C" { + #[link_name = "cyclors_0_2_1_ddsi_serdata_to_ser_ref"] pub fn ddsi_serdata_to_ser_ref( d: *const ddsi_serdata, off: usize, @@ -61,12 +63,15 @@ extern "C" { ) -> *mut ddsi_serdata; } extern "C" { + #[link_name = "cyclors_0_2_1_ddsi_serdata_unref"] pub fn ddsi_serdata_unref(serdata: *mut ddsi_serdata); } extern "C" { + #[link_name = "cyclors_0_2_1_ddsi_serdata_to_ser_unref"] pub fn ddsi_serdata_to_ser_unref(d: *mut ddsi_serdata, ref_: *const ddsrt_iovec_t); } extern "C" { + #[link_name = "cyclors_0_2_1_ddsi_serdata_from_ser_iov"] pub fn ddsi_serdata_from_ser_iov( type_: *const ddsi_sertype, kind: ddsi_serdata_kind, @@ -76,6 +81,7 @@ extern "C" { ) -> *mut ddsi_serdata; } extern "C" { + #[link_name = "cyclors_0_2_1_ddsi_serdata_from_sample"] pub fn ddsi_serdata_from_sample( type_: *const ddsi_sertype, kind: ddsi_serdata_kind, From 4e5a416402668df52c613af2929079c2d595938f Mon Sep 17 00:00:00 2001 From: Geoff Martin Date: Mon, 14 Oct 2024 12:16:34 +0100 Subject: [PATCH 2/6] Changed nm options to work with older versions of nm. --- build.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.rs b/build.rs index fa38278..5fd2c1c 100644 --- a/build.rs +++ b/build.rs @@ -215,8 +215,8 @@ fn get_defined_symbols(lib_dir: &Path, lib_name: &str) -> Result lib_path.to_str().unwrap() ); let output = Command::new("nm") - .arg("-U") - .arg("-A") + .arg("--defined-only") + .arg("--print-file-name") .arg(lib_path) .output() .expect("Failed to run nm"); From 41bc2a0a7f8417c68aebbabcc1f01c4d6d81eb98 Mon Sep 17 00:00:00 2001 From: Geoff Martin Date: Mon, 14 Oct 2024 18:08:00 +0100 Subject: [PATCH 3/6] Tidied code and set version to 0.2.2. --- Cargo.toml | 2 +- build.rs | 166 +++++++++++++++++++++++++++++------------ src/functions.template | 41 ++++++++++ src/lib.rs | 43 +---------- 4 files changed, 162 insertions(+), 90 deletions(-) create mode 100644 src/functions.template diff --git a/Cargo.toml b/Cargo.toml index 12c9be5..9ad9647 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cyclors" -version = "0.2.1" +version = "0.2.2" authors = ["kydos "] license = "Apache-2.0" readme = "README.md" diff --git a/build.rs b/build.rs index 5fd2c1c..499bb1c 100644 --- a/build.rs +++ b/build.rs @@ -1,11 +1,14 @@ extern crate bindgen; +#[allow(unused_imports)] use std::collections::HashSet; -use std::env; use std::fs::File; +#[allow(unused_imports)] use std::io::{LineWriter, Write}; use std::path::{Path, PathBuf}; +#[allow(unused_imports)] use std::process::Command; +use std::{env, fs}; fn main() { let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); @@ -143,26 +146,28 @@ fn main() { .clang_arg(format!("-I{}", cyclocut_include.to_str().unwrap())) .generate_comments(false); - // Prefix symbols in Iceoryx, Cyclone DDS and Cyclocut libraries to ensure uniqueness - #[cfg(target_os = "linux")] + #[allow(unused)] + let mut prefix = String::from(""); + + // Prefix symbols in Cyclone DDS and Cyclocut libraries to ensure uniqueness + #[cfg(all(target_os = "linux", not(feature = "iceoryx")))] { // Prefix = cyclors__ - let mut prefix = env::var("CARGO_PKG_VERSION").unwrap().replace(".", "_"); + prefix = env::var("CARGO_PKG_VERSION").unwrap().replace(".", "_"); prefix.insert_str(0, "cyclors_"); prefix.push('_'); - println!("Library prefix: {}", prefix); let mut symbols = HashSet::new(); let cyclone_symbols = get_defined_symbols(&cyclonedds_lib, "libddsc.a") .expect("Failed to get symbols from libddsc.a!"); symbols.extend(cyclone_symbols); - prefix_symbols(&cyclonedds_lib, "libddsc.a", &prefix, &symbols); + prefix_symbols(&cyclonedds_lib, "libddsc.a", &prefix, &symbols).unwrap(); let cyclocut_symbols = get_defined_symbols(&cyclocut_lib, "libcdds-util.a") .expect("Failed to get symbols from libcdds-util.a!"); symbols.extend(cyclocut_symbols); - prefix_symbols(&cyclocut_lib, "libcdds-util.a", &prefix, &symbols); + prefix_symbols(&cyclocut_lib, "libcdds-util.a", &prefix, &symbols).unwrap(); #[derive(Debug)] struct PrefixLinkNameCallback { @@ -179,7 +184,6 @@ fn main() { true => { let mut prefix = self.prefix.clone(); prefix.push_str(item_info.name); - //println!("bindgen: {prefix}"); Some(prefix) } false => None, @@ -200,6 +204,9 @@ fn main() { .clang_arg("-Wno-invalid-token-paste") .blocklist_type("^(.*IMAGE_TLS_DIRECTORY.*)$"); + // Set link name prefix on additional wrapStringper functions + generate_template_src(&prefix, &out_dir).unwrap(); + // Generate bindings let bindings = bindings.generate().expect("Unable to generate bindings"); @@ -208,62 +215,125 @@ fn main() { .expect("Couldn't write bindings!"); } +#[cfg(all(target_os = "linux", not(feature = "iceoryx")))] fn get_defined_symbols(lib_dir: &Path, lib_name: &str) -> Result, String> { let lib_path = lib_dir.to_path_buf().join(lib_name); - println!( - "Getting defined symbols from {}", - lib_path.to_str().unwrap() - ); - let output = Command::new("nm") + + let rc = Command::new("nm") .arg("--defined-only") .arg("--print-file-name") .arg(lib_path) - .output() - .expect("Failed to run nm"); - - let stdout = String::from_utf8_lossy(&output.stdout); - let stderr = String::from_utf8_lossy(&output.stderr); - - match stderr.is_empty() { - true => { - let mut result: HashSet = HashSet::new(); - for line in stdout.lines() { - let tokens: Vec<&str> = line.split_whitespace().collect(); - let symbol = *tokens.last().unwrap(); - result.insert(String::from(symbol)); + .output(); + + match rc { + Ok(output) => { + let stdout = String::from_utf8_lossy(&output.stdout); + let stderr = String::from_utf8_lossy(&output.stderr); + + match stderr.is_empty() { + true => { + let mut result: HashSet = HashSet::new(); + for line in stdout.lines() { + let tokens: Vec<&str> = line.split_whitespace().collect(); + let symbol = *tokens.last().unwrap(); + result.insert(String::from(symbol)); + } + Ok(result) + } + false => Err(format!( + "Failed to run nm on library {} (stderr: {})", + lib_name, + String::from(stderr) + )), } - Ok(result) } - false => Err(String::from(stderr)), + Err(_) => Err(format!("Failed to run nm on library {}", lib_name)), } } -fn prefix_symbols(lib_dir: &Path, lib_name: &str, prefix: &str, symbols: &HashSet) { +#[cfg(all(target_os = "linux", not(feature = "iceoryx")))] +fn prefix_symbols( + lib_dir: &Path, + lib_name: &str, + prefix: &str, + symbols: &HashSet, +) -> Result<(), String> { let mut objcopy_file_name = lib_name.to_owned(); objcopy_file_name.push_str(".objcopy"); let lib_file_path = lib_dir.to_path_buf().join(lib_name); let symbol_file_path = lib_dir.to_path_buf().join(objcopy_file_name); - let symbol_file = File::create(symbol_file_path.clone()).expect("Failed to create symbol file"); - let mut symbol_file = LineWriter::new(symbol_file); - - for symbol in symbols { - let mut symbol_arg = symbol.clone(); - symbol_arg.push(' '); - symbol_arg.push_str(prefix); - symbol_arg.push_str(symbol); - symbol_arg.push('\n'); - symbol_file - .write_all(symbol_arg.as_bytes()) - .expect("Failed to write symbol file"); + + match File::create(symbol_file_path.clone()) { + Ok(symbol_file) => { + let mut symbol_file = LineWriter::new(symbol_file); + + for symbol in symbols { + let mut symbol_arg = symbol.clone(); + symbol_arg.push(' '); + symbol_arg.push_str(prefix); + symbol_arg.push_str(symbol); + symbol_arg.push('\n'); + if symbol_file.write_all(symbol_arg.as_bytes()).is_err() { + return Err(format!( + "Failed to write symbol file for library {}", + lib_name + )); + } + } + + if symbol_file.flush().is_err() { + return Err(format!( + "Failed to write symbol file for library {}", + lib_name + )); + } + let arg = format!("--redefine-syms={}", symbol_file_path.to_str().unwrap()); + match Command::new("objcopy").arg(arg).arg(lib_file_path).output() { + Ok(_) => Ok(()), + Err(_) => Err(format!("Failed to run objcopy on library {}", lib_name)), + } + } + Err(_) => Err(format!( + "Failed to create symbol file for library {}", + lib_name + )), } - symbol_file.flush().expect("Failed to flush symbol file"); +} - let arg = format!("--redefine-syms={}", symbol_file_path.to_str().unwrap()); +fn generate_template_src(prefix: &str, out_dir: &Path) -> Result<(), String> { + let src_path = Path::new("src/functions.template"); + let dst_path = out_dir.join("functions.rs"); + + match fs::read_to_string(src_path) { + Ok(mut contents) => { + contents = contents.replace("", prefix); + + match File::create(&dst_path) { + Ok(mut file) => { + if file.write_all(contents.as_bytes()).is_err() { + let path = dst_path.to_str().unwrap(); + return Err(format!( + "Failed to write the modified content to the destination file {}", + path + )); + } - Command::new("objcopy") - .arg(arg) - .arg(lib_file_path) - .output() - .expect("Failed to run objcopy"); + println!("cargo:rerun-if-changed=src/lib.rs"); + println!("cargo:rerun-if-changed=src/functions.template"); + Ok(()) + } + Err(_) => { + let path = dst_path.to_str().unwrap(); + Err(format!( + "Failed to open the destination file ({}) for writing", + path + )) + } + } + } + Err(_) => Err(String::from( + "Failed to read the source file src/functions.template", + )), + } } diff --git a/src/functions.template b/src/functions.template new file mode 100644 index 0000000..27942ab --- /dev/null +++ b/src/functions.template @@ -0,0 +1,41 @@ +/* Additional wrapper functions for select exported inline functions */ + +extern "C" { + #[link_name = "ddsi_serdata_size"] + pub fn ddsi_serdata_size(d: *const ddsi_serdata) -> u32; +} +extern "C" { + #[link_name = "ddsi_serdata_to_ser_ref"] + pub fn ddsi_serdata_to_ser_ref( + d: *const ddsi_serdata, + off: usize, + sz: usize, + ref_: *mut ddsrt_iovec_t, + ) -> *mut ddsi_serdata; +} +extern "C" { + #[link_name = "ddsi_serdata_unref"] + pub fn ddsi_serdata_unref(serdata: *mut ddsi_serdata); +} +extern "C" { + #[link_name = "ddsi_serdata_to_ser_unref"] + pub fn ddsi_serdata_to_ser_unref(d: *mut ddsi_serdata, ref_: *const ddsrt_iovec_t); +} +extern "C" { + #[link_name = "ddsi_serdata_from_ser_iov"] + pub fn ddsi_serdata_from_ser_iov( + type_: *const ddsi_sertype, + kind: ddsi_serdata_kind, + niov: ddsrt_msg_iovlen_t, + iov: *const ddsrt_iovec_t, + size: usize, + ) -> *mut ddsi_serdata; +} +extern "C" { + #[link_name = "ddsi_serdata_from_sample"] + pub fn ddsi_serdata_from_sample( + type_: *const ddsi_sertype, + kind: ddsi_serdata_kind, + sample: *const ::std::os::raw::c_void, + ) -> *mut ddsi_serdata; +} diff --git a/src/lib.rs b/src/lib.rs index dd70615..3c475a7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,44 +47,5 @@ mod bindings { } pub use bindings::*; -/* Additional wrapper functions for select exported inline functions */ - -extern "C" { - #[link_name = "cyclors_0_2_1_ddsi_serdata_size"] - pub fn ddsi_serdata_size(d: *const ddsi_serdata) -> u32; -} -extern "C" { - #[link_name = "cyclors_0_2_1_ddsi_serdata_to_ser_ref"] - pub fn ddsi_serdata_to_ser_ref( - d: *const ddsi_serdata, - off: usize, - sz: usize, - ref_: *mut ddsrt_iovec_t, - ) -> *mut ddsi_serdata; -} -extern "C" { - #[link_name = "cyclors_0_2_1_ddsi_serdata_unref"] - pub fn ddsi_serdata_unref(serdata: *mut ddsi_serdata); -} -extern "C" { - #[link_name = "cyclors_0_2_1_ddsi_serdata_to_ser_unref"] - pub fn ddsi_serdata_to_ser_unref(d: *mut ddsi_serdata, ref_: *const ddsrt_iovec_t); -} -extern "C" { - #[link_name = "cyclors_0_2_1_ddsi_serdata_from_ser_iov"] - pub fn ddsi_serdata_from_ser_iov( - type_: *const ddsi_sertype, - kind: ddsi_serdata_kind, - niov: ddsrt_msg_iovlen_t, - iov: *const ddsrt_iovec_t, - size: usize, - ) -> *mut ddsi_serdata; -} -extern "C" { - #[link_name = "cyclors_0_2_1_ddsi_serdata_from_sample"] - pub fn ddsi_serdata_from_sample( - type_: *const ddsi_sertype, - kind: ddsi_serdata_kind, - sample: *const ::std::os::raw::c_void, - ) -> *mut ddsi_serdata; -} +// Include the generated additional wrapper functions from OUT_DIR +include!(concat!(env!("OUT_DIR"), "/functions.rs")); From 635f3e34ee07a369b308840c690089c9757e9623 Mon Sep 17 00:00:00 2001 From: Geoff Martin Date: Tue, 15 Oct 2024 11:36:01 +0100 Subject: [PATCH 4/6] Addressing clippy issue. --- build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.rs b/build.rs index 499bb1c..dc83e4e 100644 --- a/build.rs +++ b/build.rs @@ -153,7 +153,7 @@ fn main() { #[cfg(all(target_os = "linux", not(feature = "iceoryx")))] { // Prefix = cyclors__ - prefix = env::var("CARGO_PKG_VERSION").unwrap().replace(".", "_"); + prefix = env::var("CARGO_PKG_VERSION").unwrap().replace('.', "_"); prefix.insert_str(0, "cyclors_"); prefix.push('_'); From a712de0fb7bf23529e5e3533feb768d9a7fcd423 Mon Sep 17 00:00:00 2001 From: Geoff Martin Date: Tue, 15 Oct 2024 11:40:10 +0100 Subject: [PATCH 5/6] Run workflow for dev/0.2.1 branch. --- .github/workflows/rust.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 527828c..c1d7e23 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -2,9 +2,9 @@ name: Rust on: push: - branches: [master] + branches: [master, dev/0.2.1] pull_request: - branches: [master] + branches: [master, dev/0.2.1] env: CARGO_TERM_COLOR: always From c16c140ced8b7984b6b630e51d55db584aa4a30f Mon Sep 17 00:00:00 2001 From: Geoff Martin Date: Tue, 15 Oct 2024 12:25:13 +0100 Subject: [PATCH 6/6] Attempting to fix CI build issue on Windows. --- build.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build.rs b/build.rs index dc83e4e..4310032 100644 --- a/build.rs +++ b/build.rs @@ -111,6 +111,8 @@ fn main() { println!("cargo:rustc-link-lib=Iphlpapi"); #[cfg(target_os = "windows")] println!("cargo:rustc-link-lib=DbgHelp"); + #[cfg(target_os = "windows")] + println!("cargo:rustc-link-lib=bcrypt"); // Build cyclocut let cyclocut_dir = out_dir.join("cyclocut-build");