Независимый от позиции код - Position-independent code

В вычисление, позиционно-независимый код[1] (ПОС[1]) или же независимый от позиции исполняемый файл (ПИРОГ)[2] это тело Машинный код что, будучи помещенным где-то в первичная память, выполняется правильно независимо от абсолютный адрес. PIC обычно используется для общие библиотеки, так что один и тот же код библиотеки может быть загружен в место в каждом адресном пространстве программы, где он не перекрывается с другой используемой памятью (например, другими разделяемыми библиотеками). PIC также использовался в старых компьютерных системах, в которых не было MMU,[3] таким образом Операционная система могли держать приложения подальше друг от друга даже в пределах одного адресное пространство системы без MMU.

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

История

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

В IBM System / 360 (7 апреля 1964 г.) был разработан с усеченная адресация аналогично тому из UNIVAC III,[6] с учетом независимости позиции кода. При усеченной адресации адреса памяти вычисляются из базовый регистр и смещение. В начале программы программист должен установить адресуемость загрузив базовый регистр; обычно программист также сообщает ассемблеру С ПОМОЩЬЮ псевдооперация. Программист может загрузить базовый регистр из регистра, который, как известно, содержит адрес точки входа, обычно R15, или может использовать BALR (Branch And Link, форма регистрации) инструкция (со значением 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 модель 67 в (1965) для поддержки первой многозадачной операционной системы IBM TSS / 360 с разделением времени. Более поздние версии DOS / 360 (DOS / VS и т. Д.) И более поздние операционные системы IBM использовали виртуальную память. Усеченная адресация осталась частью базовой архитектуры и по-прежнему имеет преимущество, когда несколько модулей должны быть загружены в одно и то же виртуальное адресное пространство.

Другое раннее сегментированный такие системы как Берроуз MCP на Берроуз B5000 (1961) и Мультики (1964), системы подкачки, такие как IBM TSS / 360 (1967)[а] или же база и границы[b] такие системы как 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, Кнута MMIX, РУКА и x86-64 позволяют ссылаться на данные по смещению от счетчик команд. Это специально нацелено на то, чтобы сделать независимый от позиции код меньшим, менее требовательным к регистрам и, следовательно, более эффективным.

DLL Windows

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

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

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

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

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

Мультики

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

TSS

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

Динамический загрузчик[10] не загружает программные страницы и не разрешает адресные константы до отказа первой страницы.

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

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

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

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

Android включена поддержка PIE в Жевательные конфеты[18] и удалена поддержка компоновщика не-PIE в Леденец.[19]

Смотрите также

Примечания

  1. ^ Хотя TSS / 360 поддерживал общий PIC, это не относилось ко всем системам подкачки.
  2. ^ Но для каждого задания загружалась отдельная копия кода.
  3. ^ Есть некоторые технические отклонения по причинам производительности, которые выходят за рамки этой статьи.

Рекомендации

  1. ^ а б c d е ж «Типы объектного кода». Справочное руководство загрузчика приложений iRMX 86 (PDF). Intel. стр. 1-2, 1-3. Получено 2017-08-21. […] Абсолютный код, а абсолютный объектный модуль - это код, обработанный LOC86 для запуска только в определенном месте в памяти. В Загрузчик загружает абсолютный объектный модуль только в определенное место, которое модуль должен занимать. Независимый от позиции код (обычно называемый PIC) отличается от абсолютного кода тем, что PIC может быть загружен в любую ячейку памяти. Преимущество PIC перед абсолютным кодом состоит в том, что PIC не требует резервирования определенного блока памяти. Когда загрузчик загружает PIC, он получает iRMX 86 сегменты памяти из пула задания вызывающей задачи и загружает PIC в сегменты. Ограничение в отношении PIC заключается в том, что, как и в ПЛ / М-86 КОМПАКТНАЯ модель сегментации […], она может иметь только один сегмент кода и один сегмент данных, вместо того, чтобы позволять базовым адресам этих сегментов и, следовательно, самим сегментам динамически изменяться. Это означает, что программы PIC обязательно имеют длину менее 64 Кбайт. Код PIC может быть создан с помощью элемента управления BIND LINK86. Локальный код во время загрузки (обычно называемый кодом LTL) - это третья форма объектного кода. Код LTL похож на PIC в том, что код LTL может быть загружен в любом месте памяти. Однако при загрузке кода LTL загрузчик изменяет базовую часть указателей, так что указатели не зависят от начального содержимого регистров микропроцессора. Благодаря этому исправлению (корректировке базовых адресов) код LTL может использоваться задачами, имеющими более одного сегмента кода или более одного сегмента данных. Это означает, что программы LTL могут иметь длину более 64 Кбайт. FORTRAN 86 и Паскаль 86 автоматически создает код LTL даже для коротких программ. Код LTL может быть создан с помощью элемента управления BIND LINK86. […]
  2. ^ Позиционно-независимые исполняемые файлы (PIE)
  3. ^ Левин, Джон Р. (2000) [октябрь 1999]. «Глава 8: Загрузка и наложения». Линкеры и загрузчики. Серия Морган Кауфманн в программной инженерии и программировании (1-е изд.). Сан-Франциско, США: Морган Кауфманн. С. 170–171. ISBN  1-55860-496-0. OCLC  42413382. ISBN  978-1-55860-496-4. В архиве из оригинала от 05.12.2012. Получено 2020-01-12. Код: [1][2] Опечатки: [3]
  4. ^ Габерт, Александр (январь 2004 г.). "Внутреннее устройство независимого от позиции кода". Закаленный Gentoo. Получено 2009-12-03. […] Прямая адресация без поддержки PIC всегда дешевле (читай: быстрее), чем адресация PIC. […]
  5. ^ "701 объявлено", IBM, 1952-04-29
  6. ^ Справочное руководство UNIVAC III Data Processing System (PDF). Sperry Rand Corporation. 1962. УТ-2488.
  7. ^ «Достижения в управлении памятью для Windows». View.officeapps.live.com. Получено 2017-06-23.
  8. ^ «Раздел 6 Создание виртуального адреса», РУКОВОДСТВО ПО ПРОЦЕССОРАМ DPS / LEVEL 68 И DPS 8M MULTICS (PDF) (Ред. 1-е изд.), Honeywell Information Systems Inc., 1982, стр. 6–21, AL39
  9. ^ «Раздел 3: TSS для: Программиста Svslcm». Концепции и средства IBM Time Sharing System (PDF) (Седьмое изд.). Апрель 1978 г. с. 61. GC28-2003-6.
  10. ^ Динамический загрузчик системы разделения времени IBM System / 360 (PDF) (Четвертое изд.). Сентябрь 1971 г. GY28-2031-3.
  11. ^ «iphone - Двоичный файл без PIE -« Имя проекта »исполняемого файла не является независимым от позиции исполняемым файлом. - Переполнение стека». stackoverflow.com.
  12. ^ «Библиотека разработчика iOS». apple.com.
  13. ^ «Релиз OpenBSD 5.3». 2013-05-01. Получено 2020-05-09.
  14. ^ «Внимание! Обновления снимков для статической PIE». 2014-12-24. Получено 2014-12-24.
  15. ^ «Изменения / Усиление всех пакетов - FedoraProject». fedoraproject.org.
  16. ^ «Команда разработчиков Ubuntu - Еженедельный информационный бюллетень, 2017-06-15». 2017-06-15. Получено 2017-06-17.
  17. ^ «Новые профили 17.0 в репозитории Gentoo». 2017-11-30. Получено 2017-12-10.
  18. ^ «Улучшения безопасности в Android 1.5–4.1 - проект Android с открытым исходным кодом». Проект с открытым исходным кодом Android.
  19. ^ «Улучшения безопасности в Android 5.0 - проект с открытым исходным кодом Android». Проект с открытым исходным кодом Android.

внешняя ссылка