From 323134af4b6050b353c0d3175f70d2b9c795929d Mon Sep 17 00:00:00 2001 From: Geoff Martin Date: Thu, 10 Oct 2024 15:21:32 +0100 Subject: [PATCH 1/5] Initial attempt at making C library symbols unique for the master 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 b2c3b1c..4a6da5c 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()); @@ -143,6 +147,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")] @@ -157,3 +211,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 391b003..6c526a6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -51,9 +51,11 @@ pub use bindings::*; /* Additional wrapper functions for select exported inline functions */ extern "C" { + #[link_name = "cyclors_0_3_1_ddsi_serdata_size"] pub fn ddsi_serdata_size(d: *const ddsi_serdata) -> u32; } extern "C" { + #[link_name = "cyclors_0_3_1_ddsi_serdata_to_ser_ref"] pub fn ddsi_serdata_to_ser_ref( d: *const ddsi_serdata, off: usize, @@ -62,12 +64,15 @@ extern "C" { ) -> *mut ddsi_serdata; } extern "C" { + #[link_name = "cyclors_0_3_1_ddsi_serdata_unref"] pub fn ddsi_serdata_unref(serdata: *mut ddsi_serdata); } extern "C" { + #[link_name = "cyclors_0_3_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_3_1_ddsi_serdata_from_ser_iov"] pub fn ddsi_serdata_from_ser_iov( type_: *const ddsi_sertype, kind: ddsi_serdata_kind, @@ -77,6 +82,7 @@ extern "C" { ) -> *mut ddsi_serdata; } extern "C" { + #[link_name = "cyclors_0_3_1_ddsi_serdata_from_sample"] pub fn ddsi_serdata_from_sample( type_: *const ddsi_sertype, kind: ddsi_serdata_kind, From 6c0f321b040d471b995907df8fa1844b68f83528 Mon Sep 17 00:00:00 2001 From: Geoff Martin Date: Mon, 14 Oct 2024 12:15:20 +0100 Subject: [PATCH 2/5] 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 4a6da5c..864cf0a 100644 --- a/build.rs +++ b/build.rs @@ -219,8 +219,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 28d99c8671f326dc43abb45ac971584d3bd597b1 Mon Sep 17 00:00:00 2001 From: Geoff Martin Date: Mon, 14 Oct 2024 13:51:39 +0100 Subject: [PATCH 3/5] Set link name prefix for additional wrapper functions. --- build.rs | 27 ++++++++++++++++++++++++-- src/functions.template | 41 ++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 43 ++---------------------------------------- 3 files changed, 68 insertions(+), 43 deletions(-) create mode 100644 src/functions.template diff --git a/build.rs b/build.rs index 864cf0a..e76cbaf 100644 --- a/build.rs +++ b/build.rs @@ -1,11 +1,11 @@ extern crate bindgen; use std::collections::HashSet; -use std::env; use std::fs::File; use std::io::{LineWriter, Write}; use std::path::{Path, PathBuf}; use std::process::Command; +use std::{env, fs}; fn main() { let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); @@ -147,11 +147,14 @@ fn main() { .clang_arg(format!("-I{}", cyclocut_include.to_str().unwrap())) .generate_comments(false); + #[allow(unused_assignments)] + let mut prefix = String::from(""); + // 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 = env::var("CARGO_PKG_VERSION").unwrap().replace(".", "_"); prefix.insert_str(0, "cyclors_"); prefix.push('_'); println!("Library prefix: {}", prefix); @@ -204,6 +207,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); + // Generate bindings let bindings = bindings.generate().expect("Unable to generate bindings"); @@ -271,3 +277,20 @@ fn prefix_symbols(lib_dir: &Path, lib_name: &str, prefix: &str, symbols: &HashSe .output() .expect("Failed to run objcopy"); } + +fn generate_template_src(prefix: &str) { + let src_path = Path::new("src/functions.template"); + let out_dir = PathBuf::from(std::env::var("OUT_DIR").expect("OUT_DIR not set")); + let dst_path = out_dir.join("functions.rs"); + + let mut contents = fs::read_to_string(src_path).expect("Failed to read the source file."); + contents = contents.replace("", prefix); + + let mut file = + File::create(&dst_path).expect("Failed to open the destination file for writing!"); + file.write_all(contents.as_bytes()) + .expect("Failed to write the modified content to the destination file."); + + println!("cargo:rerun-if-changed=src/lib.rs"); + println!("cargo:rerun-if-changed=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 6c526a6..803cb9d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -48,44 +48,5 @@ mod bindings { } pub use bindings::*; -/* Additional wrapper functions for select exported inline functions */ - -extern "C" { - #[link_name = "cyclors_0_3_1_ddsi_serdata_size"] - pub fn ddsi_serdata_size(d: *const ddsi_serdata) -> u32; -} -extern "C" { - #[link_name = "cyclors_0_3_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_3_1_ddsi_serdata_unref"] - pub fn ddsi_serdata_unref(serdata: *mut ddsi_serdata); -} -extern "C" { - #[link_name = "cyclors_0_3_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_3_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_3_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 223909b7636df7731f7cb0ffcdb32898f7a25d20 Mon Sep 17 00:00:00 2001 From: Geoff Martin Date: Mon, 14 Oct 2024 17:01:49 +0100 Subject: [PATCH 4/5] Tidied code and set version to 0.3.2. --- Cargo.toml | 2 +- build.rs | 167 ++++++++++++++++++++++++++++++++++------------------- 2 files changed, 108 insertions(+), 61 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d21f2e9..47ed5e9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cyclors" -version = "0.3.1" +version = "0.3.2" authors = ["kydos "] license = "Apache-2.0" readme = "README.md" diff --git a/build.rs b/build.rs index e76cbaf..963a272 100644 --- a/build.rs +++ b/build.rs @@ -1,9 +1,12 @@ extern crate bindgen; +#[allow(unused_imports)] use std::collections::HashSet; 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}; @@ -147,29 +150,28 @@ fn main() { .clang_arg(format!("-I{}", cyclocut_include.to_str().unwrap())) .generate_comments(false); - #[allow(unused_assignments)] + #[allow(unused)] let mut prefix = String::from(""); - // Prefix symbols in Iceoryx, Cyclone DDS and Cyclocut libraries to ensure uniqueness - #[cfg(target_os = "linux")] + // Prefix symbols in Cyclone DDS and Cyclocut libraries to ensure uniqueness + #[cfg(all(target_os = "linux", not(feature = "iceoryx")))] { // Prefix = cyclors__ 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 { @@ -186,7 +188,6 @@ fn main() { true => { let mut prefix = self.prefix.clone(); prefix.push_str(item_info.name); - //println!("bindgen: {prefix}"); Some(prefix) } false => None, @@ -208,7 +209,7 @@ fn main() { .blocklist_type("^(.*IMAGE_TLS_DIRECTORY.*)$"); // Set link name prefix on additional wrapStringper functions - generate_template_src(&prefix); + generate_template_src(&prefix, &out_dir).unwrap(); // Generate bindings let bindings = bindings.generate().expect("Unable to generate bindings"); @@ -218,79 +219,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"); - } - symbol_file.flush().expect("Failed to flush symbol file"); - let arg = format!("--redefine-syms={}", symbol_file_path.to_str().unwrap()); + 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 + )); + } + } - Command::new("objcopy") - .arg(arg) - .arg(lib_file_path) - .output() - .expect("Failed to run objcopy"); + 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 + )), + } } -fn generate_template_src(prefix: &str) { +fn generate_template_src(prefix: &str, out_dir: &Path) -> Result<(), String> { let src_path = Path::new("src/functions.template"); - let out_dir = PathBuf::from(std::env::var("OUT_DIR").expect("OUT_DIR not set")); let dst_path = out_dir.join("functions.rs"); - let mut contents = fs::read_to_string(src_path).expect("Failed to read the source file."); - contents = contents.replace("", prefix); - - let mut file = - File::create(&dst_path).expect("Failed to open the destination file for writing!"); - file.write_all(contents.as_bytes()) - .expect("Failed to write the modified content to the destination file."); + 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 + )); + } - println!("cargo:rerun-if-changed=src/lib.rs"); - println!("cargo:rerun-if-changed=src/functions.template"); + 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", + )), + } } From b8a4f12121050009dbe7404225363277558dda45 Mon Sep 17 00:00:00 2001 From: Geoff Martin Date: Tue, 15 Oct 2024 11:33:50 +0100 Subject: [PATCH 5/5] Addressing clippy issue. --- build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.rs b/build.rs index 963a272..ead5874 100644 --- a/build.rs +++ b/build.rs @@ -157,7 +157,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('_');