mr4th-dynamic-linking/linux_linking/DETAILS.txt

118 lines
4.1 KiB
Plaintext
Raw Normal View History

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.