Skip to content

Allow usage of argc and argv when main is in a side module #13474

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Feb 17, 2021

Conversation

jprendes
Copy link
Contributor

When main is in a side module the main module assumes that main takes no arguments.
This results in the main in the side module not being able to take any input arguments.
This change always enable MAIN_READS_PARAMS when building with dynamic linking.

@jprendes jprendes marked this pull request as ready for review February 11, 2021 14:03
@sbc100
Copy link
Collaborator

sbc100 commented Feb 17, 2021

Seems reasonable I guess.

Do you have a use case for putting main in side module? Its not something that normal native build support is it?

@jprendes
Copy link
Contributor Author

Not really a use case, but a series of events that lead me to using this.

I am building a code base consisting of several shared libraries. Each shared library comes with its own googletest unit tests.
Trying to mimic the native approach, I've also build googletest as a shared library.
If my test main is in the main module, it results in memory corruption.

This is because some globals created by gtest in the main module use globals from the gtest shared library.
At the same time, globals from the gtest shared library use globals from the c++ standard library, which are in the main module.
This creates a cycle, no matter which module I initialize first, I end up with some sort of error.

I guess in a native build the c++ stdlib (as shared library) will be initialized very early on, then other shared libraries, and finally the binary (main & friends). Having main in the main module means that the c++ stdlib must be initialized at the same time as the final binary (main & friends).

As a workaround, I am compiling an empty main_module, which will always initialize first containing the c++ standard library.
Then I load the shared libraries, where they are initialized in the correct dependency order, so it ends up being:
main module (c++ std lib) -> googletest.so -> my_test.so (inc. main)

As an added bonus of that setup, I can compile all my tests as side modules, and use the same main module changing Module.dynamicLibraries at run time, meaning I only have to compile and ship the std lib only once.

@jprendes
Copy link
Contributor Author

Ah, I forgot to mention that a usual gtest main looks like this:

int main(int argc, char **argv) {
    ::testing::InitGoogleTest(&argc, argv); 
    return RUN_ALL_TESTS();
}

gtest stores the value of argc internally, and check for internal_argc > 0 to identify that it's been initialized, and complains otherwise. That's actually what told me that argc and argv weren't being sent to the side module.

@sbc100
Copy link
Collaborator

sbc100 commented Feb 17, 2021

I would strongly advise building googletest (and everything for that matter) as a static library. Especially for testing, trying mimic a shared library system with MAIN_MODULE/SIDE_MODULE split is going to be a lot of effort.

Its also slower and bigger since MAIN_MODULE=1 can't do any GC and you end up with all of libc and libc++ linking it unconditionally. In short, I would avoid MAIN_MODULE/SIDE_MODULE unless your production environment really requires it.

And yes, that fact that we have a huge amount cycles between the main module of the side module (due to stdlibs living in the main module) is another downside and another way we differ from traditional shared library systems.

@sbc100 sbc100 merged commit d996699 into emscripten-core:master Feb 17, 2021
@jprendes
Copy link
Contributor Author

I gave the static linking approach a try a while ago, unsuccessfully.

The codebase relies heavily on shared libraries through cppmicroservices, which does support static linking, but still needs dlsym to locate symbols at runtime. It also stores metadata in the shared library files and uses it at runtime to decide using an implementation from one library or the another. This last point places a strong link between a shared library and its location in the file system.

Also compilation times and memory usage (I think at link or DCE time) made things painful.

I was giving the static linking approach a go because of the lack of pthread support for dynamic linking.

In short, doing static linking brought plenty of little problems scattered everywhere, and fixing them all was too painful, so I am giving dynamic linking a try this time :-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants