Un fișier executabil este un set de instrucțiuni care determină un computer să execute o anumită sarcină [1] . Spre deosebire de un fișier text , care este conceput pentru a fi citit de un om, un fișier executabil este proiectat pentru a fi citit (și executat) de un procesor .
Prin „instrucțiuni” se înțelege în mod tradițional codul de mașină , care este executat direct de un procesor fizic [2] . În unele cazuri, un fișier care conține instrucțiuni dintr-un script de limbaj de programare intermediar (cum ar fi bytecode ) poate fi, de asemenea, considerat executabil.
Executabilele pot fi create manual în limbajul mașinii, dar această abordare nu este utilizată de obicei din cauza lipsei de sintaxă și de lizibilitate a codului ca atare, deci este mult mai convenabil să dezvoltați programe executabile într -un limbaj de programare de nivel înalt care este Ușor de înțeles. În unele cazuri, codul sursă poate fi în limbaj de asamblare , care rămâne lizibil de către om, în timp ce este proiectat să funcționeze cu instrucțiunile codului de mașină.
Codul limbajului de nivel înalt este compilat în fișiere obiect cod de mașină care nu sunt executabile. Codul poate fi apoi legat într-un fișier executabil. Acest proces se numește linking în limbajul de asamblare . Fișierele obiect, în funcție de sistemul de operare, sunt de obicei stocate într-un format de container (în care diferite date sunt conținute într-un singur fișier), cum ar fi Executable and Linkable Format (ELF) pentru sisteme similare Unix sau Portable Executable (PE) pentru Windows [3] . Oferă structură codului nativ, împărțindu-l în secțiuni precum .text (cod executabil), .data (variabile globale și statice inițiate) și .rodata (date numai în citire, cum ar fi constantele și șirurile de caractere).
Executabilele includ de obicei un timp de execuție , care implementează limbajul de programare al runtime-ului și caracteristicile compilatorului (cum ar fi programarea , gestionarea excepțiilor , apelarea constructorilor și destructorilor statici etc.) și interacțiunea cu sistemul de operare, în special trecerea argumentelor, mediile și codul returnat , împreună cu alte funcții de pornire și sfârșit de program care nu sunt specificate de programator, dar de valoare pentru lucrările ulterioare, cum ar fi execuția resurselor. În C , acest lucru este realizat de linker-ul care leagă fișierul obiect crt0 într-un fișier executabil care conține punctul de execuție , efectuează setarea și iese cu un apel la biblioteca de execuție [4] .
Astfel, fișierele executabile conțin de obicei cod de mașină suplimentar care este generat de compilator într-un anumit mod din codul sursă. Este de dorit să omiteți acest lucru în unele cazuri, de exemplu, pentru dezvoltarea sistemelor încorporate sau pur și simplu pentru a înțelege cum funcționează compilarea, conectarea și încărcarea. În C, puteți ocoli timpul de execuție standard specificând direct un script de linker, de exemplu, apelând maino funcție pentru a rula programul și returnând starea de ieșire la kernel [5] .
Pentru a fi executat de un sistem de operare , firmware sau bootloader , executabilul trebuie să se conformeze interfeței binare a aplicației (ABI) [6] . În interfețele simple, un fișier este executat prin încărcare în memorie, sărind la începutul spațiului de adrese și executând de acolo. În interfețele mai complexe, executabilele au date suplimentare care definesc un punct de intrare separat. De exemplu, în ELF, punctul de intrare este specificat în antet e_entry, care specifică adresa de memorie (virtuală) unde ar trebui să înceapă execuția. În GCC, intrarea este setată de linker folosind _start.