Implib.so icon indicating copy to clipboard operation
Implib.so copied to clipboard

Implib IMPLIB_EXPORT_SHIMS triggers an assertion when imported DSO has initializers referring to its public symbols.

Open Artem-B opened this issue 3 years ago • 3 comments

https://github.com/yugr/Implib.so/blob/bbca01e0109d51ef1b870cbc8cf953372136b8ad/arch/common/init.c.tpl#L60

When implib is built to export wrapped symbols (-DIMPLIB_EXPORT_SHIMS), the imported library initializers may end up resolving some of the symbols to the already-available symbols provided by the wrapper and that triggers an assertion checking is_lib_loading -- we're still in the middle of calling dlopen() at that point.

One way to deal with that is to dlopen() the wrapped library with RTLD_DEEPBIND. This would tell dynamic linker to bind the symbols to the library itself and avoid the unfortunate recursion. It would bypass the implib's wrapper, but I think it's the right thing to do when we build implib with visible symbols.

We may want to make it a user-controllable option, in case someone may need to guarantee that all references to a symbol go through the wrapper, but that would have to be a trade-off as in that case they would lose the ability to import libraries that refer to themselves in their initializers.

Artem-B avatar Aug 02 '22 21:08 Artem-B

Looks like the issue is more general and may not be limited to implib's use from a shared library. I've updated the title and description

Artem-B avatar Aug 02 '22 21:08 Artem-B

Yup, we ran into this during gh-15 and unfortunately I do not have a good solution for it.

One way to deal with that is to dlopen() the wrapped library with RTLD_DEEPBIND. This would tell dynamic linker to bind the symbols to the library itself and avoid the unfortunate recursion. It would bypass the implib's wrapper, but I think it's the right thing to do when we build implib with visible symbols.

Right, this is one solution. It has a disadvantage of changing the symbol resolution logic which may not be suitable for some users so I decided to not enable it by default. For now users may load library with RTLD_DEEPBIND by using --dlopen-callback.

It would be great if glibc provided us with APIs for separate library loading and ctor execution...

For completeness, the other potential solution would be to detect the is_lib_loading case and run dlopen with RTLD_NOLOAD (or locate library by hand, by searching for library via dl_iterate_phdr). The located library could then be used to call dlsym. Unfortunately it's unclear how to naturally extend this solution to --no-dlopen or --dlopen-callback cases.

yugr avatar Aug 03 '22 07:08 yugr

For completeness, the other potential solution would be to detect the is_lib_loading case and run dlopen with RTLD_NOLOAD (or locate library by hand, by searching for library via dl_iterate_phdr). The located library could then be used to call dlsym. Unfortunately it's unclear how to naturally extend this solution to --no-dlopen or --dlopen-callback cases.

Actually it's possible to just call dlopen with default arguments (i.e. without RTLD_NOLOAD) inside the constructor and it will return the loaded library. So we can expect that in most cases just calling the load callback second time when is_lib_loading is true will return the handle.

In --no-dlopen case we could search for library via dl_iterate_phdr and then dlopen it.

This is still fragile though. In particular

  • load callback needs to be reentrant
  • the approach will not work if library is loaded by custom loader

yugr avatar Sep 14 '22 07:09 yugr