Position-independent code

Position-independent code(PIC)(kód nezávislý na umístění) neboposition-independent executable(PIE)(spustitelný program nezávislý na umístění) je vevýpočetní technicestrojový kód, který lze provádět při umístění na libovolnéadresevevnitřní paměti. PIC se často používá prosdílené knihovny, takže stejný kód z knihovny může být zaveden na libovolnou adresu, která se nepřekrývá s jinou používanou pamětí (například jinou sdílenou knihovnou). PIC se také používal na starších počítačových systémech, které nemělyjednotku správy paměti(MMU),aby na systémech bez MMU mohloperační systémchránit aplikace i když jsou v jednomadresním prostoru.
Kód nezávislý na umístění lze provádět bez úprav při umístění na libovolnou adresu v paměti. Tím se liší odabsolutního kódu,který musí být zaveden na určitou adresu, aby fungoval správně,a kódurelokabilního při zavedení(LTL),v němžlinkernebozavaděčupraví adresní konstanty v programu po jeho načtení na určitou adresu, aby odpovídaly umístění na této adresa a program bylo možné spustit.Generování kódu nezávislého na umístění je často implicitním chovánímpřekladačů, ale může představovat omezení pro použití některých vlastností určitých jazyků, např. zákaz používání absolutních adres (kód nezávislý na umístění musí používatrelativní adresy). Instrukce, které se odkazují na absolutní adresy se někdy provádějí rychleji, a jejich nahrazení ekvivalentními instrukcemi, které používají relativní adresy, může způsobit poněkud pomalejší provádění, i když u moderních procesorů je rozdíl prakticky zanedbatelný.
Strojový kód prvních počítačů, např.IBM 701(uvedeného 29. dubna 1952) neboUNIVAC I(uvedeného 31. března 1951), byl pozičně závislý: program musel být sestaven tak, aby se mohl načíst a spustit na jedné určité adrese. Tyto počítače neměly operační systém a neumožňovaly multitasking. Programy se načítaly do hlavní paměti (nebo byly dokonce uloženy na magnetickém bubnu a prováděny přímo z něj) a v libovolném okmžiku mohl být spuštěn pouze jeden program. V takovém operačním kontextu nebyl kód nezávislý na umístění potřebný.
IBM System/360(uvedený 7. dubna 1964) byl již navržen sadresovánímpodobným jakoUNIVAC III,které umožňovalo použití kódu nezávislého na umístění; používá tak zvané zkrácené adresování, při němž se adresy v paměti počítají jako součet obsahubázového registrua posunutí. Na začátku programu musí programátor zajistitadresovatelnost datnaplněním bázového registru; o obsahu bázového registru programátor informuje také assembler pseudoinstrukcíUSING. Správnou hodnotu bázového registru lze získat z registru, který obsahuje adresu vstupního bodu programu (typicky R15), nebo je možné použít instrukciBALR (Branch And Link, Registr form)(s hodnotou 0 v R2), která uloží adresu následující instrukce do bázového registru, který se pak explicitně nebo implicitně používá v každé instrukci, která se odkazuje na adresy v programu. Může se používat více bázových registrů pro kód nebo data. Takové instrukce zabírají méně paměti, protože místo úplné 24-, 31-, 32- nebo 64bitové adresy (která by zabrala 4 nebo 8 bytů) obsahují číslo bázového registru (kódované 4 bity) a 12bitové posunutí, čili odkaz na dresu vyžaduje pouze dva byty.
Tato technika programování je standardem na systémech IBM S/360 a je dostupná i na novějších systémech až po IBM System/z. Při kódování v jazyce symbolických adres programátor musí zajistitadresovatelnost dat, jak je popsáno výše a další bázové registry používat pro přístup k datům v dynamicky alokované paměti. Překladače automaticky zabezpečují tento způsob adresování.
První operační systémy proIBM System/360(uvedené v roce 1966) nepoužívaly virtuální paměť (protože ji první modely System S/360 nepodporovaly), ale umožňovaly umístění programů na libovolnou (nebo automaticky zvolenou) adresu v paměti při zavádění příkazem PHASE jazykaJob Control Language.
Na systémech S/360 bez virtuální paměti tak program mohl být zaveden na jakoukoli adresu v paměti, vyžadoval však souvislý úsek paměti tak velký, aby se do něj vešel. Kvůli načítání a uvolňování paměti pro různě velké moduly mohlo docházet kfragmentaci paměti. Fragmentaci odstraňuje (resp. redukuje na velikost stránky) použití virtuální paměti.
DOS/360 aOS/360nepodporoval PIC; tranzientní volání supervisoru (SVC) v OS/360 nemohly obsahovat relokabilní adresní konstanty a mohly se spustit v libovolnému tranzientní oblasti bez provedenírelokace.
V řadě IBM System/360 byla virtuální paměť poprvé použita na Modelu 67 (v roce 1965), pro podporu prvního víceúlohového operačního systému se sdílením času TSS/360. Pozdější verze DOS/360 (DOS/VS atd.) a pozdější operační systémy firmy IBM již všechny používaly virtuální paměť. Zkrácené adresování zůstalo součástí základní architektury, a je stále výhodné, když je třeba zavést více modulů do stejného virtuálního adresního prostoru.
Pro srovnání, na prvních systémech sesegmentací paměti, např.Burroughs MCPnaBurroughs B5000(1961) aMultics(1964), a systémech se stránkováním paměti, např. IBMTSS/360(1967)nebo systémech používajícíchbázovací a mezní registr,např.GECOSnaGE 625aEXECnaUNIVAC 1107, byl kód také nezávislý na umístění, protože adresy v program byly relativní vůči aktuálnímu segmentu.
Objev dynamického překladu adres (který provádíjednotka správy paměti, MMU) původně omezoval potřebu kódu nezávislého na umístění, protože každý proces mohl mít vlastní nezávislýadresní prostor.
Spuštění více úloh současně, které používají stejný kód, způsobuje plýtvání fyzickou pamětí. Pokud dvě úlohy spouští zcela identické programy, dynamický překlad adres poskytuje řešení tím, že umožňuje systému jednoduše namapovat konkrétní adresu dvou různých úloh na stejné byty reálné paměti, která obsahuje jedinou kopii programu.
Různé programy mohou sdílet společný kód. Například program pro výpočet výplat a program pro zpracování faktur mohou obsahovat stejný třídicí podprogram. Sdílený modul (sdílená knihovna je forma sdíleného modulu) bude načten pouze jednou, a namapován do dvou adresních prostorů.
Při volání procedur ze sdílené knihovny se typicky používají pomocí malýchstubůuložených ve spojovací tabulce procedur, který volají vlastní funkci. To umožňuje především, aby sdílená knihovna mohla dědit některá volání funkcí z dříve načtené knihovny.
Reference na data z kódu nezávislého na umístění se obvykle provádějí nepřímo pomocí Globálních tabulek posunutí (anglickyGlobal Offset Tables,GOTs), které obsahují adresy všechglobálních proměnných, k nimž se přistupuje. Každá překladová jednotka nebo cílový modul má jednu GOT, která je umístěna na pevném posunutí od kód (toto posunutí není známo, dokud knihovna neníslinkována). Kdyžlinkerspojuje moduly a vytváří sdílenou knihovnu, slučuje tabulky GOT a nastavuje cílové offsety v kódu, takže není potřeba nastavovat offsety, když se sdílená knihovna později zavádí.
Pozičně nezávislé funkce přistupují ke globálním datům určením absolutní adresy tabulky GOT podle aktuální hodnoty programového čítače. Ta se často zjišťuje voláním fiktivní funkce, při kterém se uloží návratová adresa na zásobník (na architektuřeX86), do určitého standardního registru (na architekturáchSPARCaMIPS) nebo do speciálního registru (na architektuřeIBM POWER/PowerPC/Power ISA), odkud může být pak zkopírována do předdefinovaného standardního registru nebo přímo uložena do standardního registru (architekturyPA-RISC,Alfa,ESA/390az/Architecture). Některé architektury procesorů, např.Motorola 68000,Motorola 6809,WDC 65C816,KnuthůvMMIX,ARMaX86-64umožňují odkazovat se na data posunutím vůčičítači instrukcí. Cílem je, aby kód nezávislý na umístění byl menší a vyžadoval méně registrů, a tedy aby byl efektivnější.
Dynamicky linkované knihovny (DLL) vMicrosoft Windowspoužívají instrukci CALL s operačním kódem E8 (blízké volání s relativní adresou, posunutí je relativní vůči následující instrukci). Adresy v těchto instrukcích nemusí být při zavádění DLL měněny.
Některé globální proměnné (například pole řetězcových literálů, tabulky virtuálních funkcí) obsahují adresy objektů v datové sekci nebo v kódové sekci dynamické knihovny; adresy uložené v globální proměnné proto musí být aktualizovány, aby obsahovaly adresu, na kterou byla DLL zavedena. Dynamický zavaděč vypočítá adresu, na kterou ukazuje globální proměnná a výsledek uloží do této globální proměnné; tuto úpravu spouští copy-on-write paměťové stránky, která obsahující takovou globální proměnnou. Stránky s kódem a stránky s globálními proměnnými that neobsahuje ukazatele kódovat nebo globální data zůstává sdílené mezi procesy. Tato operace musí být provedena v jakémkoli OS, který může načítat dynamické knihovna na libovolnou adresu.
Ve Windows Vista a novějších verzích Windows serelokaceDLL knihoven a spustitelných programů provádí správcem paměti jádra, který umožňuje sdílení relokovaných programů více procesy. Obrazy jsou vždy relokované z upřednostňované bázové adresy, čímž se dosahujeAddress space layout randomization(ASLR).
Verze Windows před Windows Vista vyžadují, aby systémové knihovny DLL byly během linkovánípředlinkoványna nekonfliktní pevné adresy, aby nedocházelo k běhové relokaci obrazů. Běhové relokace v těchto starších verzích Windows byly prováděny DLL zavaděčem v kontextu každého procesu, a výsledné relokované části každého obrazu již nemohly být sdílené mezi procesy.
Zpracovávání DLL knihoven ve Windows se odlišuje od dřívější procedury vOS/2, z něhož bylo přebráno. OS/2 používá třetí možnost a snaží se načítat DLL, které nejsou nezávislé na umístění, do vyhrazené „sdílené oblasti“ v paměti, a mapuje je, když jsou zavedeny. Všichni uživatelé DLL mohou používat tutéž kopii v paměti.
V OSMulticsmá konceptuálněkaždá procedura kódový segment a spojovací segment. Kódový segment obsahuje pouze kód a spojovací sekci slouží jako šablona pro nový spojovací segment. Registr ukazatele 4 (PR4) ukazuje na spojovací segment procedury. Volání procedury napřed uloží PR4 na zásobník a pak do něj načte ukazatel na spojovací segment volané procedury. Volání procedur používá dvojici nepřímých ukazatelůs příznakem, který při prvním volání způsobí přerušení, v jehož obsluze dynamický spojovací mechanismus přidá novou proceduru a její spojovací segment do tabulky známých segmentů (anglickyKnown Segment Table,KST), vytvoří nový spojovací segment, zavede čísla segmentů do spojovací sekce volající procedury a vynuluje příznak ve dvojici nepřímých ukazatelů.
V IBM S/360 Time Sharing System (TSS/360 a TSS/370) může mít každá procedura veřejnou sekci CSECT pouze pro čtení a zapisovatelnou soukromou sekci prototypovou sekci (PSECT). Volající zavede V-konstantu funkce do obecného registru číslo 15 (GR15) a zkopíruje R-konstantu PSECT funkce do 19. slova úložné oblasti, na kterou ukazuje GR13.
Dynamický zavaděčnenačítá stránky programu ani neresolvuje adresní konstanty dokud nedojde k prvnímu výpadku stránky.
Spustitelné programy nezávislé na umístění(PIE) jsou spustitelné binární programy tvořená kompletně z kódu nezávislého na umístění. Zatímco některé systémy spouštějí pouze PIC spustitelné programy, existují i jiné důvody, proč jsou používány. PIE programy se používají v některýchbezpečnostně zaměřenýchdistribucíchLinuxu, které umožňují, abyPaXneboExec ShieldpoužívalAddress space layout randomizationkterá znemožňuje útočníkům zjistit, kde je proveditelný kód během bezpečnostních útoků zneužívajícíchexploity, které vyžadují znalost posunutí proveditelného kód v souboru, např. přireturn-to-libcútocích.
MacOSaIOSfirmy Apple plně podporují PIE spustitelné programy od verze 10.7 resp. 4.3; pokud spustitelný program není nezávislý na umístění vkládán do Apple App Store, ale program není zamítnut.
OpenBSDpoužívá PIE implicitně na většině architektur od OpenBSD 5.3 vydaného 1. května 2013.Podpora pro PIE ve staticky linkovaných programech, např. spustitelných programech v adresářích/bina/sbin, byl přidán ke konci roku 2014.OpenSUSE přidalo PIE jako implicitní v únoru 2015. V distribuciFedorase implicitně vytvářejí programy jako PIE od verze 23.Ubuntu17.10má PIE povoleno implicitně na všech architekturách.Nové profilyGentoo Linuxunyní podporují PIE implicitně.Od července 2017 povolujeDebianPIE implicitně.
Androidpovolil podporu PIE v verziJelly Beana odstranil podporu non-PIE linkeru vLollipop.
V tomto článku byl použitpřekladtextu z článkuPosition-independent codena anglické Wikipedii.