How to Share Global Variables in a Shared Library(.So) Across Instances of the Same Process That Use the Shared Library in Linux

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 mmaps 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 mmaped into both virtual memory spaces).

Now suppose that a.out also used libc.so.6 before forking. 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 forked 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



Leave a reply



Submit