Позиционно-независимый код

редактировать
Код машинной инструкции, который выполняется правильно независимо от того, где он находится в памяти

В вычислениях, позиционно-независимый код (PIC ) или позиционно-независимый исполняемый файл (PIE ) является телом машины код, который, будучи помещенным где-нибудь в первичной памяти, выполняется правильно независимо от его абсолютного адреса. PIC обычно используется для разделяемых библиотек, так что один и тот же код библиотеки может быть загружен в место в каждом адресном пространстве программы, где он не перекрывается с другой используемой памятью (например, другими разделяемыми библиотеками). PIC также использовался в старых компьютерных системах, в которых отсутствовал MMU, так что операционная система могла держать приложения подальше друг от друга даже в пределах единого адресного пространства система без MMU.

Позиционно-независимый код может выполняться по любому адресу памяти без изменений. Это отличается от абсолютного кода, который должен быть загружен в определенном месте для правильной работы, и кода локализации во время загрузки (LTL), в котором компоновщик или загрузчик программ изменяет программу перед выполнением, чтобы ее можно было запустить только из определенного места в памяти. Генерация независимого от позиции кода часто является поведением по умолчанию для компиляторов, но они могут накладывать ограничения на использование некоторых языковых функций, таких как запрет на использование абсолютных адресов (позиционно-независимый код должен использовать относительная адресация ). Команды, которые относятся непосредственно к конкретным адресам памяти, иногда выполняются быстрее, и замена их эквивалентными инструкциями с относительной адресацией может привести к несколько более медленному выполнению, хотя современные процессоры практически не играют роли.

Содержание
  • 1 История
  • 2 Технические детали
  • 3 Windows DLL
  • 4 Multics
  • 5 TSS
  • 6 Позиционно-независимые исполняемые файлы
  • 7 См. Также
  • 8 Примечания
  • 9 Ссылки
  • 10 Внешние ссылки
История

В ранних компьютерах, таких как IBM 701 (29 апреля 1952 г.) или UNIVAC I (31 марта 1951 г.), код зависел от позиции: каждая программа был построен для загрузки и запуска с определенного адреса. На этих ранних компьютерах не было операционной системы и многозадачности. Программы загружались в основное хранилище (или даже сохранялись на магнитном барабане для выполнения прямо оттуда) и запускались по одной за раз. В таком оперативном контексте не было необходимости в позиционно-независимом коде.

IBM System / 360 (7 апреля 1964 г.) был разработан с усеченной адресацией, аналогичной UNIVAC III, с позицией кода в виду независимость. При усеченной адресации адреса памяти вычисляются из базового регистра и смещения. В начале программы программист должен установить возможность адресации, загрузив базовый регистр; обычно программист также сообщает ассемблеру псевдооперацию USING. Программист может загрузить базовый регистр из регистра, который, как известно, содержит адрес точки входа, обычно R15, или может использовать команду BALR (Branch And Link, Register form) (со значением R2 0) для сохранить адрес следующей последовательной инструкции в базовом регистре, который затем был явно или неявно закодирован в каждой инструкции, которая ссылалась на место хранения в программе. Можно использовать несколько базовых регистров для кода или данных. Такие инструкции требуют меньше памяти, поскольку они не должны содержать полный 24, 31, 32 или 64-битный адрес (4 или 8 байтов), а вместо этого должны содержать номер базового регистра (закодированный в 4 бита) и 12-битное смещение адреса. (закодировано в 12 битах), требуя всего два байта.

Этот метод программирования является стандартным для систем типа IBM S / 360. Он использовался до сегодняшнего дня IBM System / z. При кодировании на языке ассемблера программист должен установить адресуемость программы, как описано выше, а также использовать другие базовые регистры для динамически выделяемого хранилища. Компиляторы автоматически берут на себя такую ​​адресацию.

Ранняя операционная система IBM DOS / 360 (1966) не использовала виртуальное хранилище (поскольку ранние модели System S / 360 не поддерживали его), но у нее была возможность помещать программы в произвольное (или автоматически выбранное) место хранения во время загрузки с помощью имени PHASE, оператора * JCL (язык управления заданиями).

Итак, в системах S / 360 без виртуальной памяти программа могла быть загружена в любое место хранения, но для этого требовалась непрерывная область памяти, достаточно большая для хранения этой программы. Иногда фрагментация памяти может происходить при загрузке и выгрузке модулей разного размера. Виртуальное хранилище - по дизайну - не имеет этого ограничения.

Хотя DOS / 360 и OS / 360 не поддерживали PIC, временные подпрограммы SVC в OS / 360 не могли содержать перемещаемые адресные константы и могли работать в любой из переходные зоны без переселения.

Виртуальное хранилище было впервые представлено в IBM System / 360 model 67 в (1965 г.) для поддержки первой многозадачной операционной системы и операционной системы с разделением времени TSS / 360 IBM. Все более поздние версии DOS / 360 (DOS / VS и т. Д.) И более поздние операционные системы IBM использовали виртуальную память. Усеченная адресация осталась частью базовой архитектуры и по-прежнему имеет преимущество, когда несколько модулей должны быть загружены в одно и то же виртуальное адресное пространство.

Другие ранние сегментированные системы, такие как Burroughs MCP на Burroughs B5000 (1961) и Multics (1964), системы подкачки, такие как IBM TSS / 360 (1967) или базовые и ограничивающие системы, такие как GECOS на GE 625 и EXEC на UNIVAC 1107, код также изначально не зависел от позиции, поскольку адреса в программе были относительны к текущему сегменту, а не абсолютны.

Изобретение динамической трансляции адресов (функция, обеспечиваемая MMU ) первоначально уменьшило потребность в позиционно-независимом коде, поскольку каждый процесс мог иметь собственное независимое адресное пространство (диапазон адресов). Однако одновременное выполнение нескольких заданий с использованием одного и того же кода приводит к бесполезной трате физической памяти. Если два задания запускают полностью идентичные программы, динамическое преобразование адресов обеспечивает решение, позволяя системе просто отображать адрес 32 КБ двух разных заданий в одни и те же байты реальной памяти, содержащие единственную копию программы.

Различные программы могут использовать общий код. Например, программа расчета заработной платы и программа расчета с клиентами могут содержать идентичную подпрограмму сортировки. Общий модуль (общая библиотека - это форма общего модуля) загружается один раз и отображается в два адресных пространства.

Технические детали

Вызов процедур внутри разделяемой библиотеки обычно выполняется через небольшую таблицу связывания процедур заглушки, которые затем вызывают окончательную функцию. Это, в частности, позволяет разделяемой библиотеке наследовать определенные вызовы функций из ранее загруженных библиотек, а не использовать свои собственные версии.

Ссылки на данные из позиционно-независимого кода обычно делаются косвенно, через таблицы глобального смещения (GOT), в которых хранятся адреса всех доступных глобальных переменных. На каждую единицу компиляции или объектный модуль приходится по одному GOT, и он расположен с фиксированным смещением от кода (хотя это смещение неизвестно, пока библиотека не связана ). Когда компоновщик связывает модули для создания общей библиотеки, он объединяет GOT и устанавливает окончательные смещения в коде. При последующей загрузке общей библиотеки нет необходимости настраивать смещения.

Независимые от положения функции, обращающиеся к глобальным данным, начинают с определения абсолютного адреса GOT с учетом их собственного текущего значения программного счетчика. Это часто принимает форму ложного вызова функции для получения возвращаемого значения в стеке (x86 ) или в специальном регистре (PowerPC, SPARC, MIPS, возможно, по крайней мере, некоторые другие RISC процессоры, ESA / 390 ), которые затем могут быть сохранены в предопределенном стандартном регистре. Некоторые архитектуры процессоров, такие как Motorola 68000, Motorola 6809, WDC 65C816, Knuth's MMIX, ARM и x86-64 позволяют ссылаться на данные по смещению от программного счетчика. Это специально нацелено на то, чтобы сделать независимый от позиции код меньше, потребовать меньше регистров и, следовательно, более эффективно.

Windows DLL

Библиотеки с динамической компоновкой (DLL) в Microsoft Windows используют вариант E8 инструкции CALL (вызов рядом, относительный, смещение относительно следующей инструкции). Эти инструкции не нужно исправлять при загрузке DLL.

Ожидается, что некоторые глобальные переменные (например, массивы строковых литералов, таблицы виртуальных функций) будут содержать адрес объекта в разделе данных, соответственно, в разделе кода динамической библиотеки; следовательно, сохраненный адрес в глобальной переменной должен быть обновлен, чтобы отразить адрес, по которому была загружена DLL. Динамический загрузчик вычисляет адрес, на который указывает глобальная переменная, и сохраняет значение в такой глобальной переменной; это вызывает копирование при записи страницы памяти, содержащей такую ​​глобальную переменную. Страницы с кодом и страницы с глобальными переменными, не содержащие указателей на код или глобальные данные, остаются совместно используемыми между процессами. Эта операция должна выполняться в любой ОС, которая может загружать динамическую библиотеку по произвольному адресу.

В Windows Vista и более поздних версиях Windows перемещение библиотек DLL и исполняемых файлов выполняется диспетчером памяти ядра, который разделяет перемещенные двоичные файлы между несколькими процессами. Образы всегда перемещаются со своих предпочтительных базовых адресов, достигая рандомизации структуры адресного пространства (ASLR).

Версии Windows до Vista требуют, чтобы системные библиотеки DLL были предварительно связаны на неконфликтующие фиксированные адреса во время связывания, чтобы избежать перемещения изображений во время выполнения. Перемещение среды выполнения в этих более старых версиях Windows выполняется загрузчиком DLL в контексте каждого процесса, и полученные перемещенные части каждого образа больше не могут использоваться совместно между процессами.

Работа с библиотеками DLL в Windows отличается от более ранней процедуры OS / 2, от которой она является производным. OS / 2 представляет третью альтернативу и пытается загрузить библиотеки DLL, которые не зависят от позиции, в выделенную «общую арену» в памяти и отображает их после загрузки. Все пользователи DLL могут использовать одну и ту же копию в памяти.

Multics

В Multics каждая процедура концептуально имеет сегмент кода и сегмент связи. Сегмент кода содержит только код, а раздел связи служит шаблоном для нового сегмента связи. Регистр указателя 4 (PR4) указывает на сегмент связи процедуры. Вызов процедуры сохраняет PR4 в стеке перед загрузкой его с указателем на сегмент связи вызываемого объекта. Вызов процедуры использует пару косвенных указателей с флагом, чтобы вызвать прерывание при первом вызове, чтобы механизм динамического связывания мог добавить новую процедуру и ее сегмент связывания в таблицу известных сегментов (KST), построить новый сегмент связывания, положить номера их сегментов в разделе связывания вызывающего абонента и сбросить флаг в паре косвенных указателей.

TSS

В системе разделения времени IBM S / 360 (TSS / 360 и TSS / 370) каждая процедура может иметь общедоступную CSECT только для чтения и частную секцию прототипа с возможностью записи (PSECT). Вызывающий загружает V-константу для подпрограммы в Общий регистр 15 (GR15) и копирует R-константу для PSECT подпрограммы в 19-е слово области сохранения, обозначенной как GR13.

Динамический загрузчик делает не загружать программные страницы и не разрешать адресные константы до появления ошибки первой страницы.

Позиционно-независимые исполняемые файлы

Позиционно-независимые исполняемые файлы (PIE) - это исполняемые двоичные файлы, полностью созданные из позиционно-независимого кода. Хотя некоторые системы запускают только исполняемые файлы PIC, есть и другие причины, по которым они используются. Бинарные файлы PIE используются в некоторых ориентированных на безопасность дистрибутивах Linux, чтобы позволить PaX или Exec Shield использовать рандомизацию макета адресного пространства, чтобы злоумышленники не знали, где находится существующий исполняемый код во время атаки на систему безопасности, используя эксплойты, которые полагаются на знание смещения исполняемого кода в двоичном файле, например атаки с возвратом к libc.

Apple macOS и iOS полностью поддерживают исполняемые файлы PIE, начиная с версий 10.7 и 4.3 соответственно; выдается предупреждение, когда исполняемые файлы iOS, не относящиеся к PIE, отправляются на утверждение в Apple App Store, но жестких требований пока нет и приложения, не относящиеся к PIE, не отклоняются.

OpenBSD имеет PIE включенным по умолчанию на большинстве архитектур, начиная с OpenBSD 5.3, выпущен 1 мая 2013 г. Поддержка PIE в статически связанных двоичных файлах, таких как исполняемые файлы в /bin и / sbinкаталоги, был добавлен в конце 2014 года. openSUSE добавила PIE по умолчанию в 2015-02. Начиная с Fedora 23, сопровождающие Fedora решили создавать пакеты с включенным PIE по умолчанию. В Ubuntu 17.10 PIE включен по умолчанию для всех архитектур. Новые профили Gentoo теперь поддерживают PIE по умолчанию.

Android включил поддержку PIE в Jelly Bean и удалил поддержку компоновщика без PIE в Lollipop.

См. также
  • icon Портал компьютерного программирования
Примечания
Ссылки
Внешние ссылки
Последняя правка сделана 2021-06-02 12:18:54
Содержание доступно по лицензии CC BY-SA 3.0 (если не указано иное).
Обратная связь: support@alphapedia.ru
Соглашение
О проекте