Как вы видите, в заголовках программы есть ДВА сегмента LOAD, но есть ЧЕТЫРЕ отображения памяти, почему есть еще два отображения?
Поскольку GNU_RELRO
указывает динамическому загрузчику сделать первые 0x208
байты второго PT_LOAD
сегмента доступными только для чтения.
Если вы свяжете библиотеку с gcc -shared -o lib.so lib.o -Wl,-z,norelro
, вы получите только 3 сопоставления ... Что все еще оставляет вопрос о том, почему существует 3 вместо двух?
Вы заметите, что это отображение:
7ffff7bda000-7ffff7dd9000 ---p 00001000 fd:02 18650951 /.../lib.so
на самом деле является «дырой» в пространстве процесса (доступ запрещен).
Вы также заметите, что выравнивание для второго PT_LOAD
(на самом деле для обоих) очень велико: 0x200000
.
Это сделано для обеспечения возможности работы со страницами объемом 1 МБ.
Если вы снова установите связь с gcc -shared -o lib.so lib.o -Wl,-z,norelro,-z,max-page-size=4096
, теперь у вас будут только два сопоставления, которые вы ожидаете.
Что действительно происходит в случае по умолчанию, так это то, что загрузчик должен сохранить смещение между первым и вторым PT_LOAD
(иначе двоичный файл не будет работать правильно). Таким образом, он создает большое отображение (охватывающее оба сегмента PT_LOAD
) по адресу, выбранному ядром (через mmap(0, ...)
). Затем mprotect
s область от конца первого PT_LOAD
, до конца всего отображения без доступа. И, наконец, это mmap
s второй PT_LOAD
сегмент по желаемому адресу с использованием флага MAP_FIXED
, оставляющий дыру между двумя отображениями.
Для двух сегментов LOAD, как определить, какой сегмент соответствует какой области памяти? Есть ли какой-либо стандарт или какое-либо руководство?
Вы можете довольно легко отличить от смещения. Отображения со смещением 0
соответствуют первому PT_LOAD
, отверстие не соответствует ничему, а сопоставление со смещением 00001000
соответствует второму PT_LOAD
.
кажется, что смещение основано на начальном адресе самого низкого отображения памяти
Правильно: это перемещение для всего изображения lib.so
ELF (определяется самым первым mmap(0, ...)
. Это перемещение применяется ко всем символам в изображении.
Однако существует ли какой-либо стандарт, который говорит нам, как программно определять тип значения символов (виртуальный адрес или смещение)?
Стандартов нет. Но вы можете использовать dladdr , чтобы узнать «базовый адрес» (перемещение). В частности, dli_fbase; /* Base address at which shared object is loaded */
.