'How to deal with undefined symbols when loading Mono libraries in Linux

I'm porting my Windows Mono application to Linux, one step at a time, first to WSL Linux under Windows 10, then hopefully to Real Ubuntu. Everything described here behaves identically under both WSL and Ubuntu 20.04.

My application loads the /usr/lib/libmono-2.0.so shared library, but in doing so the loader throws an exception: undefined symbol: _ZTIPi. Some research showed that this symbol was defined in libstdc++.so, so I "pre-loaded" that as well. That had no effect.

Meanwhile, I found this code fragment perusing the mono code base. It would be in mono\mini\mini-llvm.c: (https://github.com/mono/mono/blob/main/mono/mini/mini-llvm.c)

/* Add a reference to the c++ exception we throw/catch */
{
    LLVMTypeRef exc = LLVMPointerType (LLVMInt8Type (), 0);
    module->sentinel_exception = LLVMAddGlobal (module->lmodule, exc, "_ZTIPi");
    LLVMSetLinkage (module->sentinel_exception, LLVMExternalLinkage);
    mono_llvm_set_is_constant (module->sentinel_exception);
}

What exactly is happening here? What do I need to know and understand to successfully load this library?



Solution 1:[1]

Solution so far was to build and install Mono outside of the default release.

See https://www.mono-project.com/docs/compiling-mono/linux/

then One Stop Shop Build Script (Debian)

If you follow the instructions, your alternate Mono installation would be in /usr/local

This would suggest that the Mono release was somehow incorrectly compiled.

Solution 2:[2]

So, having found this after having the same issue, thank you for the information in the question and answer, it helped me figure out how to solve it. However, your solution doesn't answer your "What exactly is happening here?", which I shall try to explain.

Let's start with the _ZTIPi symbol, which when run through c++filt comes out as: typeinfo for int*

Which is indeed a part of the C++ standard library. It is listed as undefined in the default install of libmono, but not listed at all in the one I compiled from source. You can check this with something like:

$ readelf --all /usr/lib/libmono-2.0.so | grep _ZTIPi

However, libstdc++.so isn't listed as needed library in either case. You can check this with something like:

$ readelf --all /usr/lib/libmono-2.0.so | grep NEEDED

This all means that when you try to load in libmono the dynamic linker will try to resolve the _ZTIPi symbol by looking in the libraries listed by libmono (where it can't find it), and in the global symbol table for the process, which may or may not have it.

If you link against libmono at compile-time the solution would be to also link against libstdc++ at that time. This way, your executable will list libstdc++ as needed library, causing the dynamic linker to load its symbols into the global symbol table. When loading libmono the dynamic linker can then find the symbol fine.

If you load libmono dynamically at runtime, you'll have to load in libstdc++ first, using something like this:

void * cxxlib = dlopen("libstdc++.so.6", RTLD_LAZY | RTLD_GLOBAL);

Key thing here is the RTLD_GLOBAL flag, which will add all symbols to the global table, so they can be found when you're trying to load libmono.

And alternative is of course to build mono yourself, which should end you up with a libmono that isn't subtly broken. In that case, make sure to throw in a call to mono_set_dirs, like this:

mono_set_dirs("/opt/mono/lib", "/opt/mono/etc");

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 little wally
Solution 2 David Buunk