TL; DR. Почему переменные, определенные в общей библиотеке, находятся в сегментах, определенных в основной программе, а не в общей библиотеке?
Я пытаюсь понять динамическое связывание файла ELF. Я написал фиктивную общую библиотеку
// varlib.so
int x = 42;
void set_x() {
x = 16;
}
и программа, которая его использует
// 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;
}
прежде чем я посмотрел на сборку, я предполагал, что сегмент, который содержит x
, будет взят из varlib.so
(вероятно, .data
сегмент), а main.out
будет использовать его таблицу GOT (и перемещение, чтобы исправить ПОЛУЧИЛ запись в таблице) для доступа x
. Однако при осмотре я обнаружил, что
В main.out
Функция f
определяется как
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
с переездом
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
, где 0x601038 находится в разделе .bss
main.out
.
В libvar.so
set_x
определяется как
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
с переездом
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
, где 0x200fd8 находится в разделе .got
varlib.so
.
Таким образом, может показаться, что x
фактически находится в сегменте main.out
(в частности, в сегменте .bss
), и libvar.so
должен использовать свою таблицу .got
для доступа к ней. то есть полная противоположность тому, что я, хотя! Это кажется странным, поскольку x
определяется как extern
в main.out
и получает значение в varlib.so
. Я думаю, что понимаю большинство технических деталей (хотя все еще немного сбит с толку о точных значениях типов перемещения R_X86_64_COPY
и R_X86_64_GLOB_DAT
; если у кого-то есть хорошее руководство по типам перемещения, которое было бы очень полезно).
Итак, мой главный вопрос: почему я делаю это так, а не так, как я делал изначально, хотя это было сделано с x
'проживанием' в сегменте libvar.so
и main.out
доступом к нему через GOT (или какой-то другой механизм переселения)?