Я использую ящик Linux и хочу выяснить адреса символов внутри разделяемой библиотеки Position-Independent-Code во время выполнения, теперь я могу добиться этого, согласно некоторым наблюдениям, однако у меня все еще есть некоторые вопросы о программе/библиотеке загружается (да, я знаю как, но не знаю почему). Предположим, у нас есть следующие два исходных файла C:
// file: main.c
#include <stdio.h>
extern int global_field;
void main() {
printf("global field(%p) = %d\n", &global_field, global_field);
}
// file: lib.c
int global_field = 1;
И мы скомпилируем приведенный выше код с помощью следующей команды:
gcc -fPIC -g -c lib.c -o lib.o # note the -fPIC flag here
gcc -fPIC -g -c main.c -o main.o # note the -fPIC flag here
gcc -shared -o lib.so lib.o
gcc -o main main.o ./lib.so
И readelf -sW lib.so
показывает символ global_field
:
Num: Value Size Type Bind Vis Ndx Name
...
8: 0000000000201028 4 OBJECT GLOBAL DEFAULT 21 global_field
...
И readelf -lW lib.so
выводит следующие заголовки программы:
...
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x00065c 0x00065c R E 0x200000
LOAD 0x000df8 0x0000000000200df8 0x0000000000200df8 0x000234 0x000238 RW 0x200000
DYNAMIC 0x000e18 0x0000000000200e18 0x0000000000200e18 0x0001c0 0x0001c0 RW 0x8
NOTE 0x000190 0x0000000000000190 0x0000000000000190 0x000024 0x000024 R 0x4
GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW 0x10
GNU_RELRO 0x000df8 0x0000000000200df8 0x0000000000200df8 0x000208 0x000208 R 0x1
И теперь запускаем программу, она выводит следующее:
global field(0x7ffff7dda028) = 1
И cat /proc/<pid>/maps
выводит следующее:
...
7ffff7bd9000-7ffff7bda000 r-xp 00000000 fd:02 18650951 /.../lib.so
7ffff7bda000-7ffff7dd9000 ---p 00001000 fd:02 18650951 /.../lib.so
7ffff7dd9000-7ffff7dda000 r--p 00000000 fd:02 18650951 /.../lib.so
7ffff7dda000-7ffff7ddb000 rw-p 00001000 fd:02 18650951 /.../lib.so
...
Извините, здесь слишком много кода... Теперь мои вопросы:
Как видите, в заголовках программ ДВА
LOAD
сегмента, но есть ЧЕТЫРЕ сопоставления памяти, зачем еще два сопоставления?Для двух сегментов
LOAD
, как выяснить, какой сегмент соответствует какой области памяти? Есть ли какой-нибудь стандарт или какое-либо руководство?значение символа
global_field
равно0000000000201028
(см. выводreadelf -sW lib.so
), однако согласно стандарту ELF:
В исполняемых и общих объектных файлах
st_value
содержит виртуальный адрес. Чтобы сделать символы этих файлов более полезными для компоновщика времени выполнения, смещение раздела (интерпретация файла) уступает место виртуальному адресу (интерпретация памяти), для которого номер раздела не имеет значения.
Я знаю, что это позиционно-независимый код, он НЕ МОЖЕТ быть виртуальным адресом и ДОЛЖЕН быть каким-то смещением. Вычтите адрес global_field
со значением символа: 0x7ffff7dda028 - 0x201028 = 0x7ffff7bd9000
, кажется, что смещение основано на начальном адресе самого нижнего отображения памяти (см. вывод cat /proc/<pid>/maps
). Однако существует ли какой-либо стандарт, говорящий нам, как программно определить тип значения символов (виртуальный адрес или смещение)? И если это смещение, почему смещение должно основываться на этом и почему оно не основывается на своей собственной области памяти (я полагаю, что ее собственная область является последней, поскольку у нее есть разрешение на запись)?