Portable Executable
Portable Executable (PE) je souborový formát pro spustitelné soubory (EXE), objektové soubory (OBJ) a dynamické knihovny (DLL) používaný ve 32b a 64b verzích operačního systému Windows. Pojem „portable“ poukazuje na přenositelnost formátu mezi všemi 32b (a 64b) verzemi operačního systému Windows. PE formát je v zásadě datová struktura, která obsahuje informace potřebné pro zavaděč Windows: odkazy na dynamické knihovny (DLL), tabulky API export a import, resource management data a thread-local storage (TLS) data. Na operačním systému NT je PE formát použitý pro soubory EXE, DLL, OBJ, SYS a další. PE je modifikovaná verze Unixového formátu COFF.
Na operačních systémech Windows NT PE v současné době podporuje instrukční sady architektur IA-32, IA-64, a AMD64/EM64T (nebo „x86-64“). Dříve Windows 2000, Windows NT, a tedy i PE podporovaly také instrukční sady architektur MIPS, DEC Alpha a PowerPC. Protože PE je použit na Windows CE, pokračuje v podpoře několika variant architektury MIPS a také ARM a SuperH.
Přehled historie
Microsoft se přesunul na PE formát s uvedením Windows NT 3.1. Všechny pozdější verze Windows (95, 98, ME) jej podporují. Je zatížený přechodem mezi systémy založenými na DOSu a NT. Např. hlavička PE/COFF stále obsahuje program MS-DOS, který implicitně zobrazí zprávu „This program cannot be run in DOS mode“ (Tento program nemůže být spuštěn v DOS módu). Formát PE se stále vyvíjí spolu s Windows – existují rozšíření jako .NET PE, 64b verze PE32+ (občas PE+) a specifikace pro Windows CE.
Technické detaily
Soubor PE spočívá v množství hlaviček a sekcí, které společné říkají dynamickému linkeru, jak má být mapován do paměti. Protože soubor obsahuje několik různých sekcí, které potřebují různou ochranu paměti, začátek každé sekce musí být zarovnán na okraj stránky (page boundary). Např. sekce .text (kód programu) je typicky mapována jako spustitelná/pouze ke čtení (execute/readonly) a sekce .data (globální proměnné) jako nespustitelná/pro čtení i zápis (no-execute/readwrite). Nicméně, abychom se vyhnuli prázdnému místu mezi různými sekcemi, sekce nejsou na disku zarovnávány podle stránek. Dynamický linker musí mapovat každou sekci individuálně a přiřadit jí správná práva podle instrukcí v hlavičce.
Jednou ze sekcí je tzv. Import Address Table (IAT), ta je použita jako vyhledávací tabulka, pro případ, když aplikace volá funkci Windows API. Protože kompilovaný PE DLL/EXE soubor nemůže dopředu vědět, kde jsou ostatní DLL – to záleží na tom, jak byly umístěny do paměti, je potřeba použít nepřímý skok. Když dynamický linker nahrává moduly a spojuje je, zapíše všechny instrukce skoku do IAT slotů, které ukazují na aktuální umístění cílové funkce. Takto sice přidá skok navíc, za cenu vnitřního volání, ovšem snížení výkonu je většinou zanedbatelné a přinese to flexibilitu dynamických knihoven. Pokud kompiler dopředu ví, že volání bude uvnitř modulu (přes dllimport atribut), může vytvořit optimalizovaný kód, který má za následek nepřímý opkód call.
PE soubory neobsahují kód nezávislý na pozici. Místo toho jsou kompilovány s preferovanými bázovými adresami a všechny adresy generované kompilátorem/linkerem jsou pevně dány. Pokud PE soubor nemůže být nahrán na preferovanou pozici (protože je už obsazena jiným programem), operační systém změní bázové adresy (provede tzv. rebasing). Rebasing zahrnuje přepočítání všech absolutních adres a následnou modifikaci kódu pro použití s novými hodnotami. Loader poté porovná aktuální adresy s preferovanými a spočítá rozdíl. Ten je pak přidán k hodnotě preferované adresy a je spřažen s novou adresou proměnné. Všechna přemístění báze jsou uložena v seznamu a jsou přidána, když je potřeba, k existující poloze v paměti. Výsledný kód je nyní soukromý pro jeden proces a už jej nelze sdílet, čímž jsou ztraceny výhody DLL šetřící paměť a také se významně sníží rychlost nahrávání modulu. Z tohoto důvodu se rebasingu snažíme vyhnout a DLL od Microsoftu mají bázové adresy předpočítané tak, aby se nepřekrývaly. Z výše uvedeného vyplývá, že případě, že není třeba přepočítávat báze, je PE výhodný, ovšem v případě rebázování paměti může být použití nákladné. Opakem je ELF (spustitelný formát souborů pro GNU/Linux), který používá kód plně nezávislý na pozici a tabulku (global offset table), poskytne jednodušší použití paměti v budoucnosti za cenu času spouštění programu.
Externí odkazy
- Obrázky, zvuky či videa k tématu Portable Executable na Wikimedia Commons