linux_main.c defines the executable linux_main.exe it depends on load-time linking with linux_base.so it tries to perform run-time linking with linux_plugin.so linux_base.c defines the binary linux_base.so linux_plugin.c defines the binary linux_plugin.so it depends on load-time linking with linux_base.so The build script has to build linux_base.c first because it needs the results of that build to setup the load-time linking in the other builds. The linux_base.so is used to resolve load-time imported symbols. The program has a shared data structure 'int x' in the 'base' layer that is read and modified from both the 'main' layer and 'plugin' layer. The expected output if the plugin loads successfully is: ``` x = 0 provided by plugin: { x = 1 x = 2 } x = 3 ``` The expected output if the plugin is not found is: ``` x = 0 x = 1 ``` The default Linux search paths for loading binaries do not include the current directory of the process, or the path to the executable binary file. It is possible to get it to behave like Windows binary loading, but some extra steps need to be taken. (Load-Time Search Paths) (Run-Time Search Paths) ########################### Load-Time Search Paths ############################ For load-time binary dependencies, we can actually bake extra search paths into a binary. GCC's options do not cover this, but there is a backdoor in GCC for talking right to the underlying linker (ld). The backdoor is the option -Wl (lowercase L). The syntax of this option is a little unusual. As soon as a space occurs the backdoor is closed, so the entire option has to be specified without any spaces. Since we need spaces, the backdoor lets us use commas. It will remove the commas and replace them with spaces before passing the command on to ld. It looks something like this: gcc ... -Wl,-option,value ... The specific option we want to pass through this way is -rpath. This option tells the linker to bake a path into the search paths of the binary, so the syntax for specifying a path this way with the backdoor syntax is: gcc ... -Wl,-rpath,loadpath ... In order to get the same behavior as we have on Windows, we want the path to be relative to the binary itself. This can be done using the special syntax '$ORIGIN/' as the path. But there is a problem here too. The dollar sign already has a meaning in the shell, so to actually pass a raw dollar sign we actually have to escape it with a backslash. Putting it all together the option looks like this: gcc ... -Wl,-rpath,\$ORIGIN/ ... If that seems like a lot, that's because it is A LOT. It's also pretty atypical to do things this way on Linux where the system tries to have a specific place to put all the different pieces of executables. In particular you might try to put the executable in a 'bin/' folder and the shared object binaries in a 'lib/' folder. Then you would use the binary relative path like this: gcc ... -Wl,-rpath,\$ORIGIN/../lib ... ############################ Run-Time Search Paths ############################ The search paths for dlopen only include the system binary paths by default. This matters if we are calling dlopen like this: dlopen("mylayer.so", flags) In this case the search paths will not include any binary relative rules or current directory relative rules. We can use $ORIGIN like we did in the load-time case to specify paths relative to the calling binary: dlopen("$ORIGIN/mylayer.so", flags) Since this version is not just a base file name, the system search paths are ignored and the path indicated by $ORIGIN is inspected directly. We can also use . to specify the current directory like we do on the command line when we call a script or run an executable in the current directory: dlopen("./mylayer.so", flags) In this case the system will look exactly in the current directory. Finally we can use full paths to specify a directory without ambiguity: dlopen("/home/username/pluginproject/mylayer.so", flags) The nice thing about this option is that it means we can perform our own search on the file system, and assemble a full path to get exactly what we want if we have to.