I'm trying to write a plugin module in Rust for an existing C project. I've been writing the Rust bindings for the C project as I need them, and my latest one is throwing a segfault every time no matter how I try to interact with it.
Here's the C function signature I'm binding to:
switch_xml_t switch_xml_open_cfg(const char *file_path, switch_xml_t *node, switch_event_t *params)
{
switch_xml_t xml = NULL, cfg = NULL;
*node = NULL; // <-- seg-fault happens here
assert(MAIN_XML_ROOT != NULL);
if (switch_xml_locate("configuration", "configuration", "name", file_path, &xml, &cfg, params, SWITCH_FALSE) == SWITCH_STATUS_SUCCESS) {
*node = cfg;
}
return xml;
}
and here's the Rust binding and the invocation:
extern "C" {
pub fn switch_xml_open_cfg(
file_path: *const c_char,
node: *mut switch_xml_t,
params: *mut switch_event_t,
) -> switch_xml_t;
}
let mut cfg = switch_xml_t::default();
unsafe {
libfreeswitch_sys::switch_xml_open_cfg(c_str!("skeleton_rust_raw.conf"), &mut cfg, std::ptr::null_mut());
}
I've tried passing the struct to C as:
&mut cfg
Box::into_raw
pointer&mut cfg as *mut
mem::forget
on it just in case.I'm completely lost on how the pointer I'm passing in ends up null when it's dereferenced.
switch_xml_t
is a type alias:
C:
typedef struct switch_xml *switch_xml_t;
Rust:
pub type switch_xml_t = switch_xml;
For the following struct:
C:
struct switch_xml {
/*! tag name */
char *name;
/*! tag attributes { name, value, name, value, ... NULL } */
char **attr;
/*! tag character content, empty string if none */
char *txt;
/*! path to free on destroy */
char *free_path;
/*! tag offset from start of parent tag character content */
switch_size_t off;
/*! next tag with same name in this section at this depth */
switch_xml_t next;
/*! next tag with different name in same section and depth */
switch_xml_t sibling;
/*! next tag, same section and depth, in original order */
switch_xml_t ordered;
/*! head of sub tag list, NULL if none */
switch_xml_t child;
/*! parent tag, NULL if current tag is root tag */
switch_xml_t parent;
/*! flags */
uint32_t flags;
/*! is_switch_xml_root bool */
switch_bool_t is_switch_xml_root_t;
uint32_t refs;
};
Rust:
#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub struct switch_xml {
pub name: *mut c_char,
pub attr: *mut *mut c_char,
pub txt: *mut c_char,
pub free_path: *mut c_char,
pub off: switch_size_t,
pub next: *mut switch_xml_t,
pub sibling: *mut switch_xml_t,
pub ordered: *mut switch_xml_t,
pub child: *mut switch_xml_t,
pub parent: *mut switch_xml_t,
pub flags: u32,
pub is_switch_xml_root_t: switch_bool_t,
pub refs: u32,
}
The Rust definition also implements Default
:
impl Default for switch_xml {
fn default() -> Self {
switch_xml {
name: std::ptr::null_mut(),
attr: std::ptr::null_mut(),
txt: std::ptr::null_mut(),
free_path: std::ptr::null_mut(),
off: 0,
next: std::ptr::null_mut(),
sibling: std::ptr::null_mut(),
ordered: std::ptr::null_mut(),
child: std::ptr::null_mut(),
parent: std::ptr::null_mut(),
flags: 0,
is_switch_xml_root_t: switch_bool_t::SWITCH_FALSE,
refs: 0
}
}
}
pub type switch_xml_t = switch_xml;
Is not equivalent to
typedef struct switch_xml *switch_xml_t;
This typedef hides a pointer. I really don't recommend this style — one reason why is why this question exists.
The Rust side should be:
#[allow(non_camel_case_types)]
pub type switch_xml_t = *mut switch_xml;