Как общие библиотеки адресуются в памяти каждого процесса?

#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 среды.