Newbie FFI: Segfaults and undefined symbols for architecture

I've come back to working on FFI bindings for TaskWarrior (a C++ project), which I had some some incredible help on last year (thanks again!):

I had a few things working by creating a wrapper.h and wrapper.cpp thanks to @MoAlyousef and an analogous CXX example contributed by @dtolnay.

I wanted to revisit to see if I could get things working just by using the TaskWarrior header files and shared libraries directly -- which may not be possible, I suppose.

Currently, I'm focusing on the Context class, which seems to do most of the heavy lifting. I can get it to build with this

The resulting binary in ../target/debug/build/... runs and the layout tests pass.

However, it doesn't work with a simple smoke test. Looking at the generated bindings, I created two different versions of a very basic test, modeled after [taskwarrior's main.cpp code][1].

The code in question is in test_new, here.

It constructs a fake CLI argument list and just runs list; I had this part working before, so I think it's code is ok.

I was hoping to use the rusty-looking bindings, which I got to compile (but not link) like so:

let mut ctx = Context::new(Context_context);
Context::setContext(&mut ctx);
let status = ctx.initialize(i32::try_from(argv.len() - 1)?, argv.as_mut_ptr());
// let status =;
// dbg!(status);

Unfortunately, this gives me what I think is a linking error at test time, right at the first line (Context::new(Context_context);):

  = note: Undefined symbols for architecture arm64:
            "Context::Context(Context const&)", referenced from:
                taskwarrior_sys::Context::new::hbd1a16f83b64984f in taskwarrior_sys-e91a44beb3be72f4.r25ysju3oi22kyb.rcgu.o
          ld: symbol(s) not found for architecture arm64
          clang: error: linker command failed with exit code 1 (use -v to see invocation)

The generated bindings include:

extern "C" {
    #[link_name = "\u{1}__ZN7ContextC1ERKS_"]
    pub fn Context_Context(this: *mut Context, arg1: *const Context);
impl Context {
    pub unsafe fn new(arg1: *const Context) -> Self {
        let mut __bindgen_tmp = ::std::mem::MaybeUninit::uninit();
        Context_Context(__bindgen_tmp.as_mut_ptr(), arg1);

I've tried changing my a hundred different ways to allowlist_{type,function,var} .*[cC]ontext.*, but it doesn't seem to change anything.

I can see that __ZN7ContextC1ERKS_ exists in a few places:

$ rg -uuuFl '__ZN7ContextC1ERKS_' ../target/

Instead of the rusty-looking bindings, if I use the extern "C" bindings (with the underscore) directly, I get a little further:

let ctx = Context_context;
  let status = Context_initialize(
    i32::try_from(argv.len() - 1)?,
// let status = Context_run(Context_getContext());
// dbg!(status);

In this case, I get a segfault at Context_initialize.


  1. In the first case, what symbol is undefined, and why?
    • I assume Context::Context, but could it be Context (just before the const)?
    • I think I'm linking all the shared libraries and allowlisting all related types, and similar code links from the "underscore" approach -- why is it undefined?
  2. In the second case, I think I'm following pretty closely what the original cpp code does (below) -- why does it segfault from Rust?
int status {0};
Context globalContext;
Context::setContext (&globalContext);
status = Context::getContext ().initialize (argc, argv);
if (status == 0)
    status = Context::getContext ().run ();

Many thanks in advance for any pointers and suggestions!

  1. taskwarrior/main.cpp at develop · GothenburgBitFactory/taskwarrior · GitHub ↩︎

What is the definition and type of argv?

let cmd = CString::new("list")?;
let mut argv: Vec<*const c_char> =
            vec![CString::new("")?.as_ptr(), cmd.as_ptr(), std::ptr::null()];

To match up with:

int main (int argc, const char** argv)
status = Context::getContext ().initialize (argc, argv);

I haven't made any changes to argv since I had it working with the minimal C wrapper approach, which is still available on the master branch:


Allocates, gets a ptr, then at the end of the statement frees the string. So you have a use after free bug.

You should change it to CStr::from_bytes_with_nul instead

1 Like

Thanks for pointing that out!

Doesn't seem to change the behavior of the issues above, but I'm glad to know.

I believe this is the mission statement of the google/autocxx repository - have you investigated that yet?

Yes, I did about a year ago and couldn't get it to work.

Just tried again today:

fn initialize(_uhoh: BindingGenerationFailure)
autocxx bindings couldn’t be generated: Pointer pointed to something unsupported


1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.