Resolved: How does linker resolve references to data object in shared libraries at link time?

In this post, we will see how to resolve How does linker resolve references to data object in shared libraries at link time?

Question:

I am learning about linking and found a small question that I could not understand.
Consider the following files:
main.c
other.c
Then I compile these two files:
Here is part of the dissasemble of main.o:
Here is part of the disassemble of main:
They both correspond to C code:
According to the assembly, it seems that the linker has already decided the run-time address of i, because it is using a PC-relative address to reference it directly, rather than using GOT. However, as far as I know, the shared library is only loaded into memory when the program uses it loads. Thus, the executable main should have no knowledge about the address of i at link time. Then, how does the linker determine that i is located at 0x4020?
Also what does the comment i@@Base mean?

Best Answer:

According to the assembly, it seems that the linker has already decided the run-time address of i, because it is using a PC-relative address to reference it directly, rather than using GOT.


Correct.

However, as far as I know, the shared library is only loaded into memory when the program uses it loads.


Correct, except the i variable in the shared library is never used, and so its address doesn’t matter.
What happens here is described pretty well in Solaris documentation:

Suppose the link-editor is used to create a dynamic executable, and a reference to a data item is found to reside in one of the dependent shared objects. Space is allocated in the dynamic executable’s .bss, equivalent in size to the data item found in the shared object. This space is also assigned the same symbolic name as defined in the shared object. Along with this data allocation, the link-editor generates a special copy relocation record that instructs the runtime linker to copy the data from the shared object to the allocated space within the dynamic executable.


Because the symbol assigned to this space is global, it is used to satisfy any references from any shared objects. The dynamic executable inherits the data item. Any other objects within the process that make reference to this item are bound to this copy. The original data from which the copy is made effectively becomes unused.


You can observe this using readelf -Ws main:
Note that the inci() is undefined (it’s defined in libother.so), but i is defined in the main as a global symbol, and readelf -Wr main:

If you have better answer, please add a comment about this, thank you!

Source: Stackoverflow.com