To use this library, it is loaded into ROOT by using gSystem->Load(). If this library (and ROOT) are compiled with -std=c++11 and -std=c++14 it loads fine (using the same ROOT version), but if the -std=cxx17 flag is used then the following undefined error is encountered:
Does anyone understand why I am only seeing this when the -std=cxx17 flag is being used? And there any way to allow the CWB_Plugin to be undefined when it is loaded and the -std=c++17 flag is being used?
Uhm, I’m not sure why it would change the name mangling, but are you compiling ROOT and your program with the same C++ standard in all cases? This is basically a wild guess, but having ROOT compiled with one C++ standard and your program with another might “confuse” the interpreter.
You could try the following approach.
In your “library”, create a (static?) pointer that will keep the current address of the “plugin” (and any places which need it should call it using the contents of this pointer).
Add a function that will be able to set this pointer.
You will be able to load this “library” (e.g., with the pointer initialized to zero or some internal “dummy plugin”).
Then you can load your “plugin” and call this additional function which will initialize the pointer with the new address.
Maybe you do not even need any additional function. If this pointer is a global variable, ROOT itself should be able to “assign” the required value (using the loaded “plugin” address).
Thanks for @Wile_E_Coyote for your reply, I tried implementing this but it turns out that after some testing that it is not giving us the desired behaviour. Would you mind showing an example code of this approach?
After trying to create my own self-contained reproducer I found the solution, which seems to be due to the -fno-plt cflag and -Wl,-r,now linker flag being present at compile/ link time. For example, if I compile the following script (plugin_test.c):
> root -e 'gSystem->Load("plugin_test.so");exit(0)'
------------------------------------------------------------------
| Welcome to ROOT 6.26/02 https://root.cern |
| (c) 1995-2021, The ROOT Team; conception: R. Brun, F. Rademakers |
| Built for linuxx8664gcc on May 19 2022, 18:37:00 |
| From tag , 12 April 2022 |
| With |
| Try '.help', '.demo', '.license', '.credits', '.quit'/'.q' |
------------------------------------------------------------------
root [0]
cling::DynamicLibraryManager::loadLibrary(): /home/rhys.poulton/Projects/plugin_test/plugin_test.so: undefined symbol:
Reproducing the error. If I remove the -fno-plt cflag and -Wl,-r,now linker flag then I no longer see the issue. I understand why -Wl,-r,now linker flag causes this since it tries to resolve all symbols when the library is loaded (rather than when the function is called). However, it is not clear to me why -fno-plt cflag is causing this. Does anyone understand why -fno-plt cflag causes this?
As far as I understand it, this is partly explained in the man page:
(emphasis mine)
However, I don’t understand the relation to C++17 compared to C++11/14. Do you mean that switching to C++17 enables -fno-plt when building your library?
Thanks for the clarification! Yes, that does explain it.
Sorry, I should have mentioned, that there is no relation to here to the -std=c++## flags here. I suspected the -std=c++## flags initially because when I switched to another environment (a conda environment in this case with a different version of ROOT) and compared the output root-config --cflags, the -std=c++## was different so I thought that this was the cause. But now I see that this conda environment also included -Wl,-r,now in the LDFLAGS and -fno-plt in the CFLAGS causing this issue.
Great! Yes, the short answer is that use of the PLT is required to support lazy binding, i.e. symbol resolution as code is executed (see below).
TL;DR: Yes, as @hahnjo mentioned, relocations for symbols referencing functions that are not resolved when the library is loaded (e.g. if the RTLD_LAZY flag is passed to dlopen()), a PLT stub is required.
The first call to such xxx@plt function carries out the last stage of the lazy binding and patches the corresponding GOT entry accordingly; thus, the use of -fno-plt is incompatible with lazy binding.