How to share a global variable between a main process and a dynamic library via a static library (macOS)?
Doing more experiments and tweaking the linker flags led me to some other SO questions, including this and this.
Instead of -rdynamic
that works on Linux, this is what works on macOS:
The -undefined dynamic_lookup
has to be added to the linker flags of the dynamic library.
In my example, the change is as follows:
# It is important that we DO NOT link against static_lib and at the same time
# the -undefined dynamic_lookup is provided.
# target_link_libraries(dynamic_lib static_lib)
target_link_options(dynamic_lib PRIVATE -undefined dynamic_lookup)
The output that I see now is:
Hello, World!
SHARED: 0x109ead030 123
SHARED: 0x109ead030 123
Process finished with exit code 0
Does using shared libraries lead to having a single instance of global variables?
prog is linking to libA and libB is dynamically, but both of those link to libX statically. (I assume two instances?)
In this case, the answer depends on which symbols are exported from libA.so
and libB.so
.
If the variable (let's call it glob
) has static linkage, then it will not be exported and you will have two separate instances.
Likewise, if the variable doesn't have static linkage, but libX
is compiled with e.g. -fvisibility-hidden
, or if either libA.so
or libB.so
is linked with a linker script which prevents the glob
from being exported, you will have two separate instances.
However, if the variable has global linkage and its visibility is not restricted via one of the above mechanisms, then (by default) it will be exported from both libA.so
and libB.so
, and in that case all references to that variable will bind to whichever library is loaded first.
Update:
will there be two instances of that variable in memory, but just the first one is accessible, or the linker will not reserve any space at all for the second variable?
There will be two instances in memory.
When the linker builds libA.so
, or libB.so
, it has no idea what other libraries exist, and so it must reserve space in the readable and writable segment (the segment into which .data
and .bss
sections usually go) of the corresponding library.
At runtime, the loader mmap
s the entire segment, and thus has no chance of not reserving memory space for the variable in each library.
But when the code references the variable at runtime, the loader will resolve all such references to the first symbol it encounters.
Note: above is the default behavior on ELF systems. Windows DLLs behave differently, and linking libraries with -Bsymbolic
may change the outcome of symbol resolution as well.
prog is linking to libA and libB is dynamically, and both of those link to libX dynamically. (I assume one instance?)
Correct.
prog is linking to libA and libB is statically, and both of those link to libX statically. (I assume one instance again?)
This is an impossible scenario: you can't link libA.a
against libX.a
.
But when linking prog
against libA.a
, libB.a
and libX.a
, yes: you will end up with one instance of glob
.
How can two processes share the same Shared Library?
Presumably you understand page tables and copy-on-write semantics.
Suppose you run an executable a.out
, which initializes some global data, and then fork()
s. You should have little trouble understanding that all read-only (e.g. code) pages of the a.out
are now shared between two processes (the exact same pages of physical memory are mmap
ed into both virtual memory spaces).
Now suppose that a.out
also used libc.so.6
before fork
ing. You should have no trouble understanding that the read-only pages belonging to libc.so.6
are also shared between processes in exactly the same fashion.
Now suppose that you have two separate executables, a.out
and b.out
, both using libc.so.6
. Suppose a.out runs first. The dynamic loader will perform a read-only mapping of libc.so.6
into a.out
virtual memory space, and now some of its pages are in physical memory. At that point, b.out
starts, and the dynamic loader mmap
the same libc.so.6
pages into its virtual memory. Since the kernel already has a mapping for these pages, there is no reason for the kernel to create new physical pages to hold the mapping -- it can re-use previously mapped physical pages. The end result is the same as for the fork
ed binary -- the same physical pages are shared between multiple virtual memory spaces (and multiple processes).
So how can two processes have different copies of global variable,
Very simple: the read-write mappings (which are required for writable data) are not shared between processes (so that one process can write to the variable, and that write will not be visible to the other process).
Will an executable access shared-libraries' global variable via GOT?
Part of the point of a shared library is that one copy gets loaded into memory, and multiple processes can access that one copy. But every program has its own copy of each of the library's variables. If they were accessed relative to the library's GOT then those would instead be shared among the processes using the library, just like the functions are.
There are other possibilities, but it is clean and consistent for each executable to provide for itself all the variables it needs. That requires the library functions to access all of its variables with static storage duration (not just external ones) indirectly, relative to the program. This is ordinary dynamic linking, just going the opposite direction from what you usually think of.
does dynamic library shared global variable in linux
No it is not shared - the code/text section of the library is shared - the data portion is unique to each process that uses the library
Related Topics
Running Docker on Ubuntu: Mounted Host Volume Is Not Writable from Container
How to Print a String to the Terminal in X86-64 Assembly (Nasm) Without Syscall
Dynamic Loading and Weak Symbol Resolution
How to Strip Path While Archiving with Tar
How to Get the Exit Status a Loop in Bash
Deceive the Jvm About the Number of Available Cores (On Linux)
Linker Error on Linux: "Undefined Reference To"
Solution for Git Gui Client for Remote Ssh
How to Respond to Prompts in a Linux Bash Script Automatically
Use Sed to Delete All Leading/Following Blank Spaces in a Text File
How to Get Docker Container Id from Within the Container with Cgroup V2
Inotify - How to Find Out Which User Has Modified File
Clean Server Infected with C3284D Virus, Using Search and Replace