embind: EMSCRIPTEN_BINDINGS and .a files

313 views
Skip to first unread message

arnab choudhury

unread,
Dec 4, 2015, 3:12:56 PM12/4/15
to emscripten-discuss
Hi there

I am currently using embind to generate bindings for my C++ code so that it can be invoked from non ASM JS code. I have a number of .a files that my project is generating. My goal initially was to add emscripten bindings to each .a file that my project was generating since each .a file has fairly self contained functionality that can be used by consumers of my project. However, I don't see any of the bindings exposed on my Module object when I link the .a file with client code to form the final JS. Is this a known issue with embind?

To clarify, here's an example:

a. Library customString.a has a customStringBindings.cpp file that gets built into customString.a. This bindings file specifies the following binding:
EMSCRIPTEN_BINDINGS(custom_string_module) {
    class_<CustomString>("CustomString")
        .constructor<const std::string&>()
        .function("toStdString", &CustomString::ToStdString)
}

b. The final JS gets built with a link dependency on customString.a

c. I have some custom non ASM JS that attempts to create a new custom string: var customString = new Module.CustomString("tempString");

This doesn't seem to work and I see an error saying that the Module has no member named CustomString. However, if I move the bindings file into a separate CPP file that gets used in step b, I can access Module.CustomString just fine. Am I missing something here?

Thanks!
-Arnab


arnab choudhury

unread,
Dec 9, 2015, 6:48:18 PM12/9/15
to emscripten-discuss
The issue seems to be related to static variable initialization. I dumped the contents of the .a file using llvm-nm and am seeing the static binding symbols. However, libcxx doesn't seem to be initializing the static when its present in the .a file. Is this a known issue?

arnab choudhury

unread,
Dec 9, 2015, 8:06:08 PM12/9/15
to emscripten-discuss
Specifically, it looks like static class constructors for linked .a libraries are not invoked when the libcxx runtime is initialized. Since embind is relying on static class constructors being invoked at startup time, the bindings don't get generated. Here's the stack trace:

___cxx_global_var_init
__GLOBAL__sub_I_bind_cpp
asm.__GLOBAL__sub_I_bind_cpp
 __ATINIT__.push.func
callRuntimeCallbacks
ensureInitRuntime
doRun
run
...
Module.runMain

arnab choudhury

unread,
Dec 10, 2015, 5:05:12 PM12/10/15
to emscripten-discuss

So – after looking into this for a while, it turns out that static and global variable construction is not the problem.

 

The original issue was that we couldn’t use Emscripten bindings when they were built into the corresponding .a file. Emscripten bindings are initialized via a static global and it turned out that libcxx wasn’t invoking the initializer for the static global constructor for the Emscripten bindings in libtabrtcore.a. After playing around with this for a while, it turned out that in some cases, globals and statics in customString.a were indeed being initialized – which was very puzzling. It needed some more peeling of the onion to figure out what was going on.

 

It turns out that Emscripten is very smart about the code that it wants to pull in from the linked in .a files. Specifically, it will only pull in the object files from the .a file that are needed by the main executable code – even when we have no optimizations turned on. It does this by dumping the symbols for the main executable’s object file and will then identify the object file dependencies and pull in the right object files from the .a file. The LLVM linker will hence not pull in any code that’s not used when forming the final JS file.

 

Since the Emscripten bindings are not used by the object file in the main executable, they are hence ‘smartly’ discarded, and we end up not being able to access them. This means that the main executable’s object file should necessarily include any bindings we need and we cannot rely on static libraries to expose bindings. Time to move the bindings to a header file. 

Reply all
Reply to author
Forward
0 new messages