Difference between cross and zigbuild

Hello,

I have a project which is working perfectly fine when compiled with cross and segfaulting (or bus error) when compiled with zigbuild. It is always working with cross and always segfaulting (or bus error) with zigbuild. The target is aarch64-unknown-linux-gnu.

My project is using an external library (programmed in C++). I only have the header files and the *.so libraries.

I am using cxx to interface my rust code with the library.

Here is some (simplified) sample of what I have in my cpp file (for cxx) :

namespace whatever {
  void my_callback(Something *p) {}
  
  std::unique_ptr<ExternalClass> create_internal()
  {
      auto some_object = std::make_unique<ExternalClass>();
      some_object->setCallBack(my_callback); // commenting this line => no segfault / no bus error

      return some_object;
  }
  
  std::unique_ptr<MyClass> create()
  {
      auto some_object= std::make_unique<MyClass>();
  
      return some_object;
  }
  
  class MyClass::impl {
      friend MyClass;
       std::unique_ptr<ExternalClass> some_object;
  };
  
  MyClass::MyClass(): impl(new class MyClass::impl) {
      this->impl->some_object =  create_internal();
  }
}

ExternalClass is a class declared in the external lib.

in my header file:

namespace whatever {
    class MyClass{
        public :
        MyClass();
        private: 
            class impl;
            std::shared_ptr<impl> impl;
    };
   
    std::unique_ptr<MyClass> create();
}

my rust code for cxx:


#[cxx::bridge(namespace = "whatever")]
pub(crate) mod ffi {
    extern "Rust" {
        fn some_function(id: u8);
    }

    unsafe extern "C++" {
        include!("project/include/header.h");
        type MyClass;
        fn create() -> Result<UniquePtr<MyClass>>;
    }
}
unsafe impl Send for ffi::MyClass{}
unsafe impl Sync for ffi::MyClass{}

And then, I am creating my object in my rust code using ffi::create() and putting it inside a OnceLock<> to use it from anywhere.

Not sure where to look. A lot of these stuff feels like black magic to me.

The coredump stack trace is basically:

#0 0x00_BAD_ADDRESS_ZZZZZZZZZZ n/a
#1 0x00000_ADDRESS_IN_APP_YYYY _ZNSt3__110__function12__value_funcIFvP16SomethingEED2B8ne200100Ev
#2 0x00000_ADDRESS_IN_APP_XXXX _ZN8whatever21createEv 
#3 0x00000_ADDRESS_IN_APP_WWWW _ZN8whatever6MyClassC1Ev
[...]

Someone has an idea where to look?

This is probably an ABI mismatch.

The included C/C++ header is parsed by a different compiler/compiler version, or with different #defines set, or different include path, than what the external library has been compiled with, which affects struct sizes or function argument types. When C/C++ code is linked, nothing checks that the ABI is compatible. It just connects the symbols and hopes for the best, and crashes if there was a mismatch between the .h interpretation and the .so/dylib/.a linked.

Unfortunately ABI issues are hard to debug. Sprinkle the path leading to the crashing code with printf("%z\n", sizeof(Type)) for every type involved and look for differences.

Thanks for the hint.

The Callback type has different size:
printf("%zu\n", sizeof(Callback)); gives 32 for cross and 48 for zigbuild.

It is defined in a header:

typedef std::function<void(Something *pFrame)> Callback;

So the big question: why this Callback has different size and how to make it 32 with zigbuild?

Are you perhaps mixing libc++ and libstdc++? Zig probably uses libc++ as standard library for C++ it builds, while most distros use libstdc++ as standard library. They are not ABI compatible.

I have no idea, I am just using the cargo cross command or the cargo zigbuild command.
So if under the hood they are using different and incompatible lib, that would be the reason of the crash.

If this is the case, is there a way to use libstdc++ with zigbuild?

I found this github issue:

To use stdlibc++, zig must be called with something like (command from the github issue):
zig c++ base.cc $(pkg-config gtkmm-4.0 --cflags --libs) -o gtk_test /usr/lib/libstdc++.so /usr/lib/libgcc_s.so -I/usr/include/c++/13.2.1/x86_64-pc-linux-gnu -I/usr/include/c++/13.2.1 -nostdinc++ -nostdlib++ -L/usr/lib/gcc/x86_64-pc-linux-gnu/13.2.1 -lgcc

Now the question is: is there a possibility to gives zigbuild the rights flags to make it work ^^

Maybe with some "cargo::rustc-xxxxx" stuff. I have to investigate this.

Found this issue for cargo zigbuild:

So, I'm not sure if this is doable from my end.

Too bad. I love compiling with zigbuild, it is faster and I don't need a docker install.