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

Позиційно-незалежний код може виконуватися за будь-якою адресою пам'яті без модифікації. Це відрізняє його від абсолютного коду, який для правильної роботи має бути завантажений у певне місце, та коду, адреси якого можуть бути визначені під час завантаження (LTL), у якому компонувальник або завантажувач програми модифікує програму перед виконанням, завдяки чому її можна запустити лише з певного місця в пам'яті. Останні терміни іноді називають позиційно-залежним кодом. Створення позиційно-незалежного коду часто є типовою поведінкою компіляторів, але вони можуть накладати обмеження на використання деяких можливостей мови, наприклад забороняти використання абсолютних адрес (позиційно-незалежний код має використовувати відносну адресацію). Інструкції, які безпосередньо звертаються до конкретних адрес пам'яті, іноді виконуються швидше, і заміна їх еквівалентними інструкціями з відносною адресацією може призвести до дещо повільнішого виконання, хоча сучасні процесори роблять цю різницю практично незначною.

SunOS 4.x та ELF

Виклики процедур усередині спільної бібліотеки зазвичай здійснюються через невеликі заглушки таблиці зв'язування процедур (PLT), які потім викликають відповідну функцію. Це, зокрема, дозволяє спільній бібліотеці успадковувати певні виклики функцій від раніше завантажених бібліотек замість використання власних версій.

Звертання до даних із позиційно-незалежного коду зазвичай здійснюються опосередковано, через (GOT), які зберігають адреси всіх доступних глобальних змінних. Для кожної одиниці компіляції або об'єктного модуля існує одна GOT, і вона розташована на фіксованому зміщенні відносно коду (хоча це зміщення невідоме до моменту компонування бібліотеки). Коли компонувальник компонує модулі для створення спільної бібліотеки, він об'єднує GOT і встановлює остаточні зміщення в коді. Немає потреби коригувати зміщення під час подальшого завантаження спільної бібліотеки.

Позиційно-незалежний код, який звертається до глобальних даних, робить це шляхом отримання адреси глобальної змінної з її запису в GOT. Оскільки GOT розташована на фіксованому зміщенні відносно коду, зміщення між адресою певної інструкції в коді та адресою запису GOT для певної глобальної змінної також є фіксованим, тому це зміщення не потрібно змінювати залежно від адреси, за якою завантажується позиційно-незалежний код. Інструкція, яка отримує запис GOT для глобальної змінної, використовувала б режим адресації, що містить зміщення відносно певної інструкції в коді; це може бути адресація відносно лічильника команд (PC), якщо архітектура системи команд її підтримує, або адресація відносно регістра, коли функції завантажують у цей регістр адресу інструкції у пролозі функції.

DLL у Windows

Динамічно компоновані бібліотеки (DLL) у Microsoft Windows використовують варіант E8 інструкції CALL (Call near, відносне зміщення відносно наступної інструкції). Ці інструкції не потребують модифікації під час завантаження DLL.

Очікується, що деякі глобальні змінні (наприклад, масиви рядкових літералів, таблиці віртуальних функцій) міститимуть адресу об'єкта відповідно в секції даних або секції коду динамічної бібліотеки; тому збережену в глобальній змінній адресу необхідно оновити відповідно до адреси, за якою було завантажено DLL. Динамічний завантажувач обчислює адресу, на яку посилається глобальна змінна, і зберігає це значення в такій глобальній змінній; це спричиняє копіювання під час запису сторінки пам'яті, що містить таку глобальну змінну. Сторінки з кодом і сторінки з глобальними змінними, які не містять вказівників на код або глобальні дані, залишаються спільними для процесів. Цю операцію необхідно виконувати в будь-якій ОС, яка може завантажувати динамічну бібліотеку за довільною адресою.

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

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

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

Див. також Динамічний лінкер Об'єктний модуль Сегмент коду

Примітки