#c #linux #executable #elf
#c #linux #исполняемый файл #elf
Вопрос:
Из того, что я понимаю:
Когда вы компилируете код в двоичный файл, нет необходимости переводить адреса в этом двоичном файле. Поскольку каждый процесс имеет свое собственное пространство памяти, адреса, используемые в двоичном файле, могут быть использованы во время выполнения.
Однако, если у вас есть общая библиотека, как она отображается в этом пространстве памяти процессов? Если код библиотеки использует адреса виртуальной памяти, их придется менять для каждого процесса, где библиотека сопоставляется с другим адресом виртуальной памяти.
Я не очень разбираюсь в этих вещах (как вы, наверное, можете догадаться), так что извините, если что-то невероятно неправильно.
Заранее спасибо.
Комментарии:
1. Я не знаю о Linux, но, насколько я понимаю, в Windows, в процессе 2, если ему необходимо загрузить (общую) dll, уже загруженную в процесс 1, образ памяти является общим, если диапазон адресов, в который загружается dll в процессе 1, доступен в процессе 2, в противном случаев памяти находятся две копии образа, по одной на каждый базовый адрес. Исполняемый файл, являющийся первым модулем, загруженным в каждое адресное пространство (за исключением ядра, которое всегда присутствует), всегда загружается по своему предпочтительному адресу, обычно 0x400000.
2. об этом заботится ОС и ее MMU подробнее читайте
3. Вы хотите посмотреть на позиционно-независимый код и исправления загрузчика, но это, вероятно, выходит за рамки stackoverflow.
4. Не уверен, что я смотрю в правильном направлении, но я думаю, что меня интересует, как работает динамический компоновщик и как он преобразует символы в адреса во время выполнения
5. @user1937198 Я не думаю, что это выходит за рамки переполнения стека
Ответ №1:
В Linux ссылки на общие библиотеки разрешаются по умолчанию, когда библиотека эффективно вызывается в вашем коде. Это называется ленивым выжиданием. Таким образом, все двоичные файлы не являются исполняемыми процессором. Большинство из них фактически интерпретируются (см. /lib64/ld-linux-*.so
).
Чтобы выполнить это, двоичный файл ELF содержит две определенные таблицы :
- таблица привязки процедур (PLT)
- Глобальная таблица смещений (GOT)
Выполняемый вами код ссылается на PLT, который выполняет перенаправления. При первом вызове GOT будет содержать адрес обратного вызова, который при выполнении переходит к загрузчику, который преобразует адрес в динамическую библиотеку. Библиотека отображается в виртуальной памяти вашей программы, даже если она присутствует только один раз в вашей физической памяти.
Вы используете виртуальную память, поэтому адреса, которые видят ваши процессы, вероятно, будут разными, поэтому использование одного GOT для каждого процесса. Что касается использования двух таблиц: это в основном из соображений безопасности, поэтому вы никогда не выполняете инструкции с доступной для записи страницы.
Вы можете отключить отложенное ожидание, если хотите, установив переменную LD_BIND_NOW
среды.