Hier wordt beschreven hoe de Linux® binaire
compatibiliteit werkt. Het meeste van wat nu volgt is sterk gebaseerd op een
e-mailbericht van Terry Lambert <tlambert@primenet.com>
aan FreeBSD
babbel mailinglijst (Message ID: <199906020108.SAA07001@usr09.primenet.com>).
FreeBSD heeft een abstractie met de naam “execution class loader”. Dit is een wig in de systeemaanroep execve(2).
Wat er gebeurt is dat FreeBSD een lijst van loaders heeft, in plaats van een enkele loader die terugvalt op de #! loader voor het draaien van elke shellinterpreter of shellscript.
Vroeger onderzocht de enige loader op het UNIX® platform het magische getal (in het algemeen de eerste 4 of 8 bytes van het bestand) om te zien of het een binary was die het systeem kende en als dat het geval was laadde het de binaire loader.
Als het niet het binaire type voor het systeem was, faalde de aanroep naar execve(2) en probeerde de shell het als shellopdrachten uit te voeren.
Deze aanname was een standaard voor “wat de huidige shell ook is.”
Later werd er een hack gemaakt voor sh(1) om de eerste twee tekens te onderzoeken en als die bestonden uit :\n voerde het in plaats hiervan de csh(1) shell uit (het idee is dat SCO de hack als eerste maakte).
Wat FreeBSD nu doet is door een lijst van loaders gaan met een generieke #! loader die kennis heeft van interpreters in de zin van de karakters die volgen op de volgende witruimte tot de laatste, met uiteindelijk een terugval op /bin/sh.
Voor Linux ABI-ondersteuning ziet FreeBSD het magische getal als een ELF-binary (het maakt op dit punt geen onderscheid tussen FreeBSD, Solaris™, Linux of elk ander besturingssysteem dat een ELF-beeldtype heeft).
De ELF loader zoekt naar een gespecialiseerd merk, dat een commentaargedeelte in het ELF-beeld is en dat niet aanwezig is in SVR4/Solaris ELF-binairen.
Om Linux-binairen werkend te krijgen, moeten ze gemerkt worden als het type Linux met brandelf(1):
# brandelf -t Linux bestand
Als dit gedaan is, ziet de ELF loader het Linux-merk in het bestand.
Als de ELF loader het Linux-merk tegenkomt, verplaatst de loader een pointer in de proc-structuur. Alle systeemaanroepen worden met deze pointer geïndexeerd (in een traditioneel UNIX systeem is dit de sysent[]-structuurarray, die de systeemaanroepen bevat). Ook wordt het proces gemerkt voor speciale behandeling door de valstrikvector van de signaal-trampolinecode samen met nog meer (kleine) aanpassingen die door de Linux kernelmodule worden afgehandeld.
De Linux kernelmodule bevat naast andere dingen een lijst van sysent[]-ingangen waarvan de adressen in de kernelmodule staan.
Als een systeemaanroep door de Linux-binary wordt aangeroepen, verwijdert de valstrikcode de referentie aan de functiepointer van de systeemaanroep en geeft die de ingangspunten van de systeemaanroep van Linux en niet van FreeBSD.
Verder reroot de Linux-modus dynamisch lookups. Dit is wat de optie union
(niet het unionfs
bestandssysteemtype!) voor het aankoppelen van bestandssystemen effectief doet.
Eerst wordt een poging gedaan om het bestand in de map /compat/linux/origineel-pad
op te zoeken en vervolgens
alleen als dat mislukt, wordt het bestand in /origineel-pad opgezocht. Dit zorgt ervoor dat
binairen die andere binairen nodig hebben kunnen draaien (zo kan bijvoorbeeld de
Linux-gereedschapskist geheel onder Linux ABI-ondersteuning draaien). Dit betekent ook dat
Linux-binairen FreeBSD-binairen kunnen laden en
draaien als er geen overeenkomende Linux-binairen
zijn en dat er een uname(1)-opdracht in
de mappenstructuur /compat/linux gezet kan worden om
er zeker van te zijn dat Linux-binairen niet kunnen
weten dat ze niet op Linux draaien.
Effectief bevindt er zich een Linux-kernel in de FreeBSD-kernel. De verschillende onderliggende functies die alle functies implementeren die de kernel aanbiedt, zijn dezelfde tabelingangen voor de systeemaanroepen van FreeBSD als van Linux: bestandssysteembewerkingen, bewerkingen op het virtuële geheugen, signaalaflevering, System V IPC, enzovoort. Het enige verschil is dat FreeBSD-binairen de lijm functies voor FreeBSD krijgen en dat de Linux-binairen de lijm-functies voor Linux krijgen (de meeste oudere besturingssystemen hadden alleen hun eigen lijm-functies: adressen van functies die in een statische globale sysent[] structuurarray werden opgeslagen, in plaats van adressen van functies waarvan dynamisch een geïnitialiseerde pointer wordt verwijderd in de proc-structuur van het proces dat de aanroep doet).
Welke is de eigenlijke FreeBSD ABI? Dat maakt niet uit. Eigenlijk is het enige verschil dat (op dit moment; dit kan eenvoudig veranderen in een toekomstige uitgave, en dat gebeurt waarschijnlijk na deze uitgave) de lijm-functies van FreeBSD statisch gelinkt zijn in de kernel en dat de lijm-functies van Linux zowel statisch gelinkt kunnen worden als dat ze door een kernelmodule worden benaderd.
Maar is dit nu echt emulatie? Nee. Het is een ABI-implementatie, geen emulatie. Er is geen emulator (of simulator, om de volgende vraag voor te zijn) bij betrokken.
Dus waarom wordt het dan soms “Linux-emulatie” genoemd? Om het moeilijk te maken om FreeBSD te verkopen! Serieus, het is zo omdat de historische implementatie in een tijd werd gedaan toen er echt geen ander woord was om te beschrijven wat er aan de hand was, om te zeggen dat FreeBSD Linux-binairen draaide was niet waar als de code niet in de kernel gecompileerd werd of als een module geladen werd en er moest een woord zijn voor hetgeen geladen werd. Vandaar “de Linux-emulator”.