I'm trying to do some slightly strange linking and getting some very strange errors.
Context:
I work with a tool that generates some C libraries based on a project from the vendor. This produces a library of common functions and a library of functions specific to the project.
I want to create a tool that helps you compile these C files in Rust and access the common functions and specific functions.
To do this I have the common crate. This includes a routine for building the libraries with CC and then has wrapping functions for the common functions we expect to be built.
So we end up with:
user crate -- builds --> common lib -- linked by --> common crate.
Problem:
In the common crate I have a session struct and I can use an extern block to reference the common functions in that struct and that builds and runs without issue.
If I attempt to use one of those functions in a different context I get linker errors. By this I mean:
- Calling a function in a drop implementation.
- Calling a function in a OnceCell initialisation.
As soon as I do this I get link errors for the symbols used in that impl block. I don't see the library in the link list.
Does the order of dependencies somehow change because I'm using outside traits?
This is the session code. If I comment out the drop impl then no build error:
//! Holds session management functions for the FPGA.
//!
use std::sync::Once;
use crate::error::{to_fpga_result, NiFpga_Status};
static LIB_INIT: Once = Once::new();
extern "C" {
fn NiFpga_Initialize() -> i32;
//fn NiFpga_Finalize() -> NiFpga_Status;
fn NiFpga_Open(
bitfile: *const i8,
signature: *const i8,
resource: *const i8,
attribute: u32,
session: *mut SessionHandle,
) -> NiFpga_Status;
fn NiFpga_Reset(session: SessionHandle) -> NiFpga_Status;
fn NiFpga_Run(session: SessionHandle) -> NiFpga_Status;
fn NiFpga_Close(session: SessionHandle, attribute: u32) -> NiFpga_Status;
fn NiFpga_ReadU8(session: SessionHandle, offset: u32, value: *mut u8) -> NiFpga_Status;
fn NiFpga_WriteU8(session: SessionHandle, offset: u32, value: u8) -> NiFpga_Status;
}
pub type SessionHandle = u32;
pub struct Session {
pub handle: SessionHandle,
}
impl Session {
pub fn new(
bitfile: &str,
signature: &str,
resource: &str,
) -> Result<Self, crate::error::FPGAError> {
LIB_INIT.call_once(|| unsafe {
//NiFpga_Initialize();
});
unsafe {
NiFpga_Initialize();
}
let mut handle: SessionHandle = 0;
let bitfile = std::ffi::CString::new(bitfile).unwrap();
let signature = std::ffi::CString::new(signature).unwrap();
let resource = std::ffi::CString::new(resource).unwrap();
let result = unsafe {
NiFpga_Open(
bitfile.as_ptr(),
signature.as_ptr(),
resource.as_ptr(),
0,
&mut handle,
)
};
to_fpga_result(Self { handle }, result)
}
pub fn reset(&mut self) -> Result<(), crate::error::FPGAError> {
println!("reset");
let result = unsafe { NiFpga_Reset(self.handle) };
to_fpga_result((), result)
}
pub fn run(&mut self) -> Result<(), crate::error::FPGAError> {
let result = unsafe { NiFpga_Run(self.handle) };
to_fpga_result((), result)
}
pub fn close(self) -> Result<(), crate::error::FPGAError> {
let result = unsafe { NiFpga_Close(self.handle, 0) };
to_fpga_result((), 0)
}
pub fn read_u8(&self, offset: u32) -> Result<u8, crate::error::FPGAError> {
let mut value: u8 = 0;
let result = unsafe { NiFpga_ReadU8(self.handle, offset, &mut value) };
to_fpga_result(value, result)
}
pub fn write_u8(&self, offset: u32, value: u8) -> Result<(), crate::error::FPGAError> {
let result = unsafe { NiFpga_WriteU8(self.handle, offset, value) };
to_fpga_result((), result)
}
}
impl Drop for Session {
fn drop(&mut self) {
unsafe {
NiFpga_Close(self.handle, 0);
}
}
}