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.