Trying to use Plugin feature with ROOT6 with -std=c++17

I am trying to run a ROOT6 code with the -std=c++17 which has a plugin feature defined as follows:

extern void CWB_Plugin(TFile* jfile, CWB::config*, network*, WSeries<double>*, TString, int);

where this code is purposely left undefined when the code is compiled so it can be defined at runtime:

nm -C "/home/rhys.poulton/packages/library/tools/install/lib64/" | grep CWB_Plugin
                 U CWB_Plugin(TFile*, CWB::config*, network*, WSeries<double>*, TString, int)

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:

root [0] gSystem->Load("/home/rhys.poulton/packages/library/tools/install/lib64/")
cling::DynamicLibraryManager::loadLibrary(): /home/rhys.poulton/packages/library/tools/install/lib64/ undefined symbol: _Z10CWB_PluginP5TFilePN3CWB6configEP7networkP7WSeriesIdE7TStringi

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?

Thanks in advance for any help!

Please read tips for efficient and successful posting and posting code

ROOT Version: 6.24/6
Platform: Linux x86_64 gcc
Compiler: GCC 9.4.0

root-config --cflags

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.

The program inherits the C++ standard from ROOT so I should always be the same version

$ root-config --cflags
-pthread -std=c++17 -m64 -I/home/rhys.poulton/.conda/envs/build_cwb/include

I guess you first need to load the “plugin” and then the “library” which requires it.

I would like to avoid that if possible as it would require large restructuring of code.

Do you have any idea why I am only seeing this with -std=c++17?

Is it possible to tell ROOT when loading this library to ignore this function being undefined?

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).

I really can’t tell, maybe @pcanal or @Axel have an idea :confused:

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?

@Axel I’m not sure if ROOT itself has any “built-in” feature which could be “reused”.

If not, there are plenty of examples: Uncle Google → C++ plugin

(also feel free to provide a self-contained reproducer that we can debug that shows the problematic behavior in C++17)

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):

extern void CWB_Plugin();

int main(int argc, char* argv[]) {
    return 0;

using the following command

> g++ -fno-plt -c plugin_test.cxx -o plugin_test.o

and then create the .SO file by doing:

> g++ -shared plugin_test.o -o

Then load this .so into ROOT:

> root -e 'gSystem->Load("");exit(0)'
  | Welcome to ROOT 6.26/02               |
  | (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/ 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?

1 Like

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.

1 Like

Hi @Rhys_Poulton,

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.


1 Like