TL;DR Why do variables defined in a shared library seem to reside in segments defined in the main program and not the shared library?
I am trying to understand ELF file dynamic linking. I wrote a dummy shared library
// varlib.so
int x = 42;
void set_x() {
x = 16;
}
and a program that uses it
// main.out
#include <stdlib.h>
#include <stdio.h>
extern int x;
void set_x();
int f() {
return x;
}
int main(int argc, char** argv) {
set_x();
printf("%d\n", f());
return 0;
}
before I looked at the assembly I assumed that the segment that holds x
would come from varlib.so
(probably the .data
segment) and main.out
would use it's GOT table (and a relocation to fix up the GOT table entry) to access x
. However on inspection I find that
In main.out
The function f
is defined as
0000000000400637 <f>:
400637: 55 push rbp
400638: 48 89 e5 mov rbp,rsp
40063b: 8b 05 f7 09 20 00 mov eax,DWORD PTR [rip+0x2009f7] # 601038 <x>
400641: 5d pop rbp
400642: c3 ret
with relocation
Relocation section '.rela.dyn' at offset 0x490 contains 3 entries:
Offset Info Type Symbol's Value Symbol's Name + Addend
0000000000601038 0000000600000005 R_X86_64_COPY 0000000000601038 x + 0
where 0x601038 is in the .bss
section of main.out
.
In libvar.so
set_x
is defined as
00000000000005aa <set_x>:
5aa: 55 push rbp
5ab: 48 89 e5 mov rbp,rsp
5ae: 48 8b 05 23 0a 20 00 mov rax,QWORD PTR [rip+0x200a23] # 200fd8 <x-0x48>
5b5: c7 00 10 00 00 00 mov DWORD PTR [rax],0x10
5bb: 90 nop
5bc: 5d pop rbp
5bd: c3 ret
with relocation
Relocation section '.rela.dyn' at offset 0x3d0 contains 8 entries:
Offset Info Type Symbol's Value Symbol's Name + Addend
0000000000200fd8 0000000500000006 R_X86_64_GLOB_DAT 0000000000201020 x + 0
where 0x200fd8 is in the .got
section of varlib.so
.
So it would seem that x
is actually located in a segment of main.out
(specifically the .bss
segment) and libvar.so
has to use it's .got
table to access it. i.e. the exact opposite of what I though! This seems odd as x
is defined as extern
in main.out
and given a value in varlib.so
. I think I understand most of the technical details (although am still a bit confused about the exact meanings of the R_X86_64_COPY
and R_X86_64_GLOB_DAT
relocation types; If anyone has a good guide on relocation types that would be much appreciated).
So my main question is why do it this way and not the way I originally though it was done with x
'living' in a libvar.so
segment and main.out
accessing it through the GOT (or some other relocation mechanism)?