Shared libraries maken

ArticleCategory:

Software Development

AuthorImage:

[Foto van de Auteur]

TranslationInfo:

original in en Luis Colorado

en to nl Tom Uijldert

AboutTheAuthor:

Abstract:

Dit artikel geeft een korte historie van het programmeren en gaat verder in detail in op het maken van functiebibliotheken en de ondersteuning die Linux daarbij levert.

ArticleIllustration:

[Illustratie]

ArticleBody:

1. Het maken van een programma. Introductie.

Het maken van hedendaagse programma's is het gevolg van een evolutie van de gewoontes en ervaringen, opgedaan in het verleden door programmeurs en ontwerpers.

Dit proces bestaat uit de volgende stappen:

Aanmaken van de code in een hogere programmeertaal met een tekst-editor. Voor grotere programma's is het niet haalbaar om alles in één bestand te stoppen, dus worden ze opgesplitst in delen die functioneel bij elkaar horen. Het is niet noodzakelijk dat alle broncode in dezelfde taal geschreven wordt. Sommige talen zijn immers beter geschikt voor het oplossen van een specifiek programmeerprobleem dan andere.

Na het schrijven van de code voor het programma dienen deze bestanden vertaald te worden naar machine-uitvoerbare code, gewoonlijk object code genoemd. Deze object code bevat de instructies uit de broncode in een formaat dat door de computer is uit te voeren. Deze vertaalslag heet compilatie. De compilatie wordt uitgevoerd voor elk broncode bestand. De gecompileerde object code kan een programma bevatten, een subroutine, variabelen, enz. -- al deze object code bestanden worden dan gebruikt bij de volgende stap.

Nadat van alle bronbestanden object code bestanden gemaakt zijn moeten deze aan elkaar worden gelinkt. Dit wordt gedaan door de linker. De linker zorgt ervoor dat de externe referenties worden "opgelost". De code in één module kan verwijzen naar een subroutine of variabele in een andere module, en het is aan de linker om uit te zoeken of deze verwijzingen inderdaad kloppen. Indien alle verwijzingen kloppen, produceert de linker een programma dat kan worden uitgevoerd.

Het uitvoeren van een programma wordt gedaan door een speciaal onderdeel van het besturingssysteem, en in het geval van Linux is dat de systeemfunctie exec(). Deze functie zoekt het programma op de harddisk, kent een hoeveelheid geheugen aan dit programma toe, en laadt specifieke delen van het programma (de eigenlijke code en de waarde van de variabelen) in het geheugen. Vervolgens wordt het programma gestart.

2. De geschiedenis van het programmeerproces.

Het programmeerproces is steeds geëvolueerd om telkens het meest efficiënt om te springen met de mogelijkheden van de computer.

De eerste programma's werden direct in machinecode geschreven. Later zag men in dat het vertalen van een in een hogere programmeertaal geschreven programma eenvoudig geautomatiseerd kon worden. Dit verhoogde uiteraard de productiviteit van de programmeur.

Toen deze automatische vertaling van broncode naar object code was gerealiseerd (de evolutie van de compilatie is hier versimpeld, dit is een erg complex proces) bestond het programmeerproces uit het maken van de broncode, de compilatie en het uitvoeren van het programma als laatste stap.

Daarna zag men al snel dat de compilatie erg veel eiste van de computer, zoals processortijd, en dat er veel identieke functies waren die door verschillende programma's gebruikt werden. Bovendien, wanneer een klein onderdeel van het programma gewijzigd werd, betekende dat de volledige hercompilatie van het programma, ook van de delen die niet waren gewijzigd.

Daarom introduceerde men het idee van compilatie per module. Hierbij scheidde men het hoofdprogramma van de functies die door verschillende andere programma's gebruikt werden. Deze functies waren al gecompileerd en de object code werd bewaard (een voorloper op een functiebibliotheek of library).

Men kon aldus programma's ontwikkelen die deze functies gebruikten zonder deze functies steeds opnieuw te compileren. Het bleef een complex proces, omdat de linker precies verteld moest worden welke functies er uit de functiebibliotheek moesten worden meegelinkt (ook de functies die de programmeur niet direct in het hoofdprogramma gebruikt had, maar die gebruikt werden door andere functies uit de functiebibliotheek).

3. Wat is een functiebibliotheek?

Het bovenstaande probleem werd opgelost door het maken van functiebibliotheken. Dit is een speciaal type bestand (meer bepaald een archief, zoals tar(1) of cpio(1)), in een formaat dat de linker begrijpt. Wanneer nu een programma gelinkt wordt met deze functiebibliotheek, zal de linker alleen die modules, benodigd voor het programma, uit de bibliotheek halen. Dit leidde tot een ander voordeel: grote functiebibliotheken konden worden gebruikt bij het schrijven van een programma; de programmeur hoefde niet alle afhankelijkheden van de verschillende functies in de bibliotheek uit te zoeken.

De functionaliteit van bibliotheken, zoals hierboven beschreven, is niet veel verder geëvolueerd. Het is efficiënter gemaakt door aan het begin van de bibliotheek een 'inhoudsopgave' toe te voegen, zodat de linker niet iedere keer de gehele bibliotheek moet doorlopen om te zien of een bepaalde functie in de bibliotheek zit. Het toevoegen van zo'n inhoudsopgave (symbol-table) wordt in Linux gedaan met het commando ranlib(1). De hierboven beschreven bibliotheken worden statische bibliotheken genoemd (static libraries).

Na de introductie van multitasking computers werd het idee voor het delen van code (code sharing) geïntroduceerd. Als, op dezelfde computer, tegelijkertijd meerdere keren hetzelfde programma uitgevoerd werd, werd het programma slechts één keer geladen (normaal gesproken wijzigt een programma z'n eigen code niet). Dit bespaart uiteraard veel geheugen op grote multi-user systemen.

Dit idee werd nog verder uitgebreid (ik weet niet wie dit bedacht heeft, maar het is een geweldige vondst ;-) Vaak gebruiken verschillende programma's dezelfde functies uit dezelfde bibliotheek, maar omdat het verschillende programma's zijn bevat het computergeheugen enkele malen dezelfde code. We zouden natuurlijk nog meer geheugen kunnen besparen indien deze code die door verschillende programma's gebruikt werd, maar één keer in het geheugen zit.

Dit maakt het linken uiteraard moeilijker. Het programma wordt niet volledig gelinkt, maar bepaalde delen worden pas gelinkt op het moment dat het programma wordt uitgevoerd. De linker (in het geval van linux, ld (1)) ziet dat de code gebruik maakt van 'gedeelde' bibliotheken (shared libraries), en zal de code voor de gebruikte functies niet meelinken. Het besturingssysteem zelf, door middel van de exec() systeemaanroep, herkent een programma dat gebruik maakt van gedeelde bibliotheken, en voert speciale code hiervoor uit, onder meer om deze gedeelde bibliotheken te laden (het toekennen van gedeeld geheugen voor de code, eigen geheugen voor de variabelen, enz.). Dit is de vereenvoudigde versie van wat er gebeurd wanneer er een programma wordt uitgevoerd.

Wanneer een programma wordt gelinkt dat gebruik maakt van statische bibliotheken zal de linker uiteraard functioneren zoals eerder beschreven.

De gedeelde bibliotheek is geen archief van object code bestanden, maar een bestand die de object code zelf bevat. Tijdens het linken van een programma dat gebruik maakt van gedeelde bibliotheken zal de linker niet deze gedeelde bibliotheken doorlopen om te zien welke modules er al dan niet aan het programma moeten worden toegevoegd. De linker zal er alleen voor zorgen dat de referenties naar functies en variabelen die zich in de bibliotheken bevinden, worden opgelost, door alleen de naam van de bibliotheek in het programma op te nemen. Men zou een archief ar(1) bibliotheek kunnen maken van alle gedeelde bibliotheken, maar dit wordt niet vaak gedaan, omdat een gedeelde bibliotheek pas wordt geladen op het moment van uitvoeren, niet op het moment van linken. Misschien is gedeeld object een betere benaming dan gedeelde bibliotheek (omdat gedeelde bibliotheek een ingeburgerde term is, zullen we echter deze laatste naam blijven gebruiken).

4. De verschillende bibliotheek-typen.

Zoals eerder vermeld zijn er bij Linux twee verschillende typen bibliotheken, statische en gedeelde. Een statische bibliotheek is een verzameling modules die m.b.v. ar(1) in een archief geplaatst zijn, en door middel van ranlib(1) zijn geïndexeerd. De naam van zo'n bibliotheek eindigt vaak op .a (ik gebruik expres de term extensie niet, omdat het concept van extensie bij Linux niet bestaat). De linker herkent het .a bestand als een bibliotheek, en doorzoekt deze naar de benodigde functies en variabelen voor het programma.

Gedeelde bibliotheken daarentegen, zijn geen archieven, maar objecten, gemarkeerd door een speciale code (die ze als gedeelde bibliotheek markeert). De linker ld(1), voegt deze modules, zoals eerder beschreven, niet toe aan het programma, maar beschouwt de referenties wel als opgelost. Indien de gedeelde bibliotheek zelf referenties maakt naar andere functies, al dan niet in een gedeelde of statische bibliotheek, probeert de linker deze ook op te lossen. De linker herkent een gedeelde bibliotheek aan een bestandsnaam die eindigt op .so (niet .so.xxx.yyy, hier komen we later nog op terug).

5. Het linken onder Linux.

Ieder programma bestaat uit één of meerdere object modules die gelinkt worden door ld(1), de Linux linker.

ld(1) heeft vele commando-regel opties om het linken te beïnvloeden, we beperken ons hier tot de verschillende opties die te maken hebben met bibliotheken. ld(1) wordt niet direct door de programmeur opgeroepen, maar door de compiler gcc(1) zelf, nadat de source code in object code vertaald is. Een oppervlakkige kennis van de modus operandi helpt ons het gebruik van bibliotheken met Linux beter te begrijpen.

Om te kunnen linken heeft ld(1) een lijst van bibliotheken en object code files nodig. Deze lijst kan in een willekeurige volgorde worden gegeven* zolang we de bovengenoemde conventies volgen: .so is een gedeelde bibliotheek; .a is een statische bibliotheek, en .o is een object code bestand.

* Dit is niet helemaal waar. Alleen die modules die op dat moment nodig zijn uit een bibliotheek worden door ld(1) gelinkt. Als er later in de lijst een object bestand refereert aan een eerdere bibliotheek, kan dit voor problemen zorgen. De volgorde van de bibliotheken kan dus wel belangrijk zijn.

Aan de andere kant staat ld(1) ons toe om standaard bibliotheken te specificeren via de opties -l en -L.

Maar... wat verstaan we onder standaard bibliotheken, wat is het verschil? Er is geen verschil. Standaard bibliotheken echter, staan op voorgedefinieerde plaatsen die ld(1) doorzoekt, terwijl de bibliotheken die op de commando-regel worden meegegeven via hun bestandsnaam worden gezocht.

De bibliotheken die standaard doorzocht worden zijn alle bibliotheken die in de directories /lib en /usr/lib staan (het schijnt dat er versies of implementaties van ld(1) zijn die ook nog andere plaatsen gebruiken). Met de optie -L wordt de opgegeven directory ook doorzocht. Gewoonlijk wordt voor elke extra directory die ook moet worden doorzocht -L directory toegevoegd. De -I optie (-I Naam, waar Naam de bibliotheek is) zorgt er dan weer voor dat ook de bibliotheek libNaam wordt gebruikt. De linker tracht eerst de gedeelde bibliotheek, libNaam.so te vinden. Wordt deze niet gevonden, dan zoekt de linker naar de statische bibliotheek, libNaam.a.

Als ld(1) de gedeelde bibliotheek libNaam.so vind, wordt de gedeelde bibliotheek gelinkt, terwijl als de statische versie libNaam.a wordt gevonden, de statische bibliotheek wordt gelinkt.

6. Dynamisch Linken en laden van Gedeelde Bibliotheken

Het dynamisch linken wordt pas gedaan op het moment dat een programma wordt uitgevoerd door een speciale module (deze module is zelf ook een gedeelde bibliotheek), genaamd /lib/ld-linux.so

Eigenlijk zijn er twee modules die dit kunnen doen: /lib/ld.so (voor bibliotheken die het oude a.out formaat nog gebruiken) en /lib/ld-linux.so (voor bibliotheken in het nieuwe ELF formaat).

Deze modules worden telkens geladen als een programma gebruik maakt van gedeelde bibliotheken. De namen zijn standaard; ze moeten niet verplaatst worden naar een andere directory, of hernoemd. Als we /lib/ld-linux.so een andere naam zouden geven kunnen we geen programma's meer starten die gebruik maken van gedeelde bibliotheken, omdat die deze module nodig hebben om de referenties naar de gedeelde bibliotheken te helpen oplossen.

Deze module maakt gebruik van het bestand /etc/ld.so.cache, waarin bijgehouden wordt waar de gedeelde bibliotheken zich bevinden. Hierop komen we dadelijk nog terug.

7. soname. Versies van Bibliotheken. Compatibiliteit.

Nu komen we aan bij een van de heikele punten in verband met gedeelde bibliotheken: de versies.

Een foutmelding die vaak voorkomt is 'library libX11.so.3 not found'. Frustrerend, want we hebben toch een hoger versienummer, namelijk libX11.so.6 maar toch werkt het niet. Hoe komt het nu dat ld.so(8) geen problemen geeft wanneer we libpepe.so.45.0.1 door libpepe.so.45.22.3 vervangen, maar steen en been klaagt als we deze door libpepe.so.46.22.3 vervangen?

Onder Linux (en alle besturingssystemen die gebruik maken van het ELF formaat) worden de verschillende bibliotheken onderscheiden door een unieke karakter-string: de soname

De soname staat in de bibliotheek zelf en wordt toegekend op het moment dat de bibliotheek gemaakt wordt. Hiervoor wordt aan de linker een optie meegegeven (-soname <bibliotheeknaam>).

Deze karakter-string wordt gebruikt door de dynamische lader (/lib/ld.so of /lib/ld- linux.so) om de juiste gedeelde bibliotheken op te zoeken voor het programma. Dit gaat als volgt: ld-linux.so ziet dat het programma een gedeelde bibliotheek gebruikt en bepaalt de soname. Dan wordt /etc/ld.so.cache doorzocht totdat deze bibliotheek gevonden wordt. Als de juiste versie van de bibliotheek niet gevonden wordt, zal de foutmelding 'libXXX.so.Y' not found gegeven worden. De dynamische linker is dus op zoek naar een bibliotheek met de soname libXXX.so.Y en niet naar een bibliotheek met bestandsnaam libXXX.so.Y

Dit kan tot verwarring leiden, dit probleem blijft namelijk bestaan als we de bibliotheek een andere naam geven. Het is mogelijk om de soname van een bibliotheek te veranderen, maar dit is geen goed idee, omdat de toekenning van de soname aan een bibliotheek de volgende conventie volgt:

We spreken af dat de soname van een bibliotheek de juiste bibliotheek en interface van de bibliotheek identificeert. Indien de interface gelijk blijft bij een wijziging van een bibliotheek (aantal functies, variabelen en parameters blijft gelijk), dan zijn de bibliotheken onderling verwisselbaar, en in het algemeen stellen we dat er slechts kleine wijzigingen aan de bibliotheek zijn geweest. Als dit gebeurd zal het 'minor number' van de bibliotheek wijzigen (bijvoorbeeld libc.so.5.1 naar libc.so.5.3)

Echter, wanneer er functies worden toegevoegd of weggelaten, en in het algemeen de interface wordt gewijzigd, is het niet mogelijk om de bibliotheek uitwisselbaar te houden met vorige versies (bij voorbeeld libX11.so.3 vervangen door libX11.so.6 is een deel van de upgrade van X11R5 naar X11R6, waarin nieuwe functies werden toegevoegd, en zo de interface wijzigde). De verandering van X11R6-v3.1.2 naar X11R6-v3.1.3 zal naar alle waarschijnlijkheid de interface niet wijzigen, en dus zal de bibliotheek dezelfde soname hebben -- maar om de oude bibliotheek van de nieuwe te onderscheiden zal deze een andere bestandsnaam hebben (daarom zie je het versienummer volledig in de bestandsnaam maar niet in de soname).

8. ldconfig(8)

Zoals eerder reeds vermeld wordt /etc/ld.so.cache gebruikt door ld-linux.so om de juiste bibliotheken op te zoeken. Dit is een binair bestand om redenen van efficiëntie en wordt gemaakt door de utitlity ldconfig(8).
ldconfig(8) maakt een symbolische link voor elke gedeelde bibliotheek in de directoires die in /etc/ld.so.conf staan, naar de soname van de bibliotheek. Hierdoor kan ld.so aan de hand van de soname de juiste bibliotheek vinden. Het is daarom alleen noodzakelijk om ldconfig(8) uit te voeren op het moment dat we een directory aan de lijst toevoegen, niet als we een bibliotheek in een reeds vermelde directory toevoegen.

9. Ik wil een gedeelde bibliotheek maken

Voordat je een gedeelde bibliotheek gaat maken moet je eerst nagaan of dat wel nuttig is. Een gedeelde bibliotheek kan er voor zorgen dat je systeem trager wordt om de volgende redenen:
Het laden van een programma gebeurt in verschillende stappen: eerst wordt het hoofdprogramma geladen, vervolgens worden de verschillende gedeelde bibliotheken geladen die het hoofdprogramma gebruikt (we zullen zien dat dit laatste mechanisme in ons voordeel zal werken).

De dynamische bibliotheken moeten code bevatten die onafhankelijk is van de plaats in het geheugen. De plaats in het geheugen van de virtuele adresruimte van een proces is immers niet bekend totdat de code wordt geladen. De compiler is aldus gedwongen een register te reserveren voor het bijhouden van het laadadres van de bibliotheek. Dit register is daarmee niet beschikbaar voor het gebruik in optimalisatie van de code. Dit is een geringe straf, het zal hooguit 5% schelen in de snelheid van de code.

De code van een goede dynamische bibliotheek is het grootste deel van de tijd in gebruik door welk programma dan ook (daarmee voorkom je het opnieuw inladen van de code nadat het proces wat het heeft opgestart weer is geëindigd. Zolang er andere processen zijn die dit gebruiken zal de code ingeladen blijven).

De gedeelde bibliotheek wordt volledig in het geheugen geladen (niet alleen de modules die worden gebruikt). Om van nut te zijn moet de bibliotheek dus in zijn geheel bruikbaar zijn. Het slechtst denkbare voorbeeld is een dynamische bibliotheek waarin één functie wordt gebruikt en de andere 90% nauwelijks.

Een goed voorbeeld van een dynamische bibliotheek is de standaard bibliotheek van C (C standard library). Het wordt gebruikt door alle programma's die in C zijn geschreven. Gemiddeld wordt iedere functie hieruit wel een keer gebruikt op diverse plaatsen.

In een statische bibliotheek is het onnodig om functies op te nemen die sporadisch worden gebruikt. Zolang deze functies in hun eigen module zijn opgenomen, zullen ze niet worden meegelinkt door programma's die ze niet nodig hebben.

9.1 Compileren van de code

Het compileren gaat op dezelfde wijze als bij doorsnee code, behalve dan dat we de optie '-f PIC meegeven (PIC = Position Independent Code) om instructies te genereren die onafhankelijk zijn van de plaats in het geheugen.

Dit is een fundamentele stap omdat normaal gesproken de geheugenadressen al tijdens het linken worden toegewezen (en dus op een vastgesteld tijdstip). In het oude a.out formaat was dit onmogelijk. Iedere gedeelde bibliotheek werd dus een vaste plaats in de virtuele adresruimte toegewezen. Dit gaf automatisch problemen op het moment dat een programma twee gedeelde bibliotheken wilde gebruiken en de geheugenruimtes over elkaar heen lagen. Men was dus gedwongen een reserveringslijst centraal te administreren om vast te leggen welke bibliotheek welk gedeelte van de adresruimte had gereserveerd om zo overlappen te voorkomen.

Zoals reeds gezegd is deze administratie niet meer nodig omdat nu bij dynamische bibliotheken de plaats in het geheugen wordt bepaald op het moment van inladen/gebruik. De code moet dus adresonafhankelijk zijn.

9.2 Linken van objecten in de bibliotheek.

Na het compileren moet de gegenereerde code worden gelinkt met een speciale optie die ervoor zorgt dat de code dynamisch kan worden geladen. Zoals de lezer kan zien lijkt dit op een normale link-operatie, afgezien van een aantal opties die zorgen voor het creëren van een dynamische bibliotheek. Laten we de opties één voor één behandelen:
-shared
Dit vertelt de linker dat hij uiteindelijk een gedeelde bibliotheek moet genereren. De uitvoer (een executable) zal dus gemarkeerd worden als zijnde een bibliotheek.

-o libNaam.so.xxx.yyy.zzz
Geeft de naam van het uiteindelijke bestand. Het is niet strikt noodzakelijk deze naamgeving te volgen maar als je dit een standaard bibliotheek wilt maken, geschikt voor toekomstige ontwikkelingen, dan is het wel handig als je dit doet.

-Wl,-soname,libNaam.so.xxx
-Wl vertelt aan gcc(1) dat de volgende opties (gescheiden door een komma) voor de linker zijn bedoeld. Dit is het mechanisme dat door gcc(1) wordt gebruikt om ld(1) van opties te voorzien. Hierboven worden de volgende opties aan de linker meegegeven:

	-soname libNaam.so.xxx
Deze optie stelt de naam (soname) van de bibliotheek vast zodat deze alleen door programma's kan worden aangeroepen die om een bibliotheek vragen met die soname.

9.3 Installeren van de bibliotheek

We hebben nu een executeerbare versie. Nu nog installeren op de juiste plaats om hem te kunnen gebruiken.

Om een programma te compileren dat gebruikt maakt van onze bibliotheek moet het volgende commando worden gebruikt:

	$ gcc -o programma libNaam.so.xxx.yyy.zzz
of, als de library op de daartoe bestemde plek staat (/usr/lib), kan worden volstaan met:
	$ gcc -o programma -lNaam
(als de library in /usr/local/lib had gestaan dan had kunnen worden volstaan met de toevoeging -L/usr/local/lib). Doe het volgende voor het installeren van de bibliotheek:
Kopieer de bibliotheek naar de directory /lib of /usr/lib. Als je hem in een andere directory zet (bijvoorbeeld /usr/local/lib) dan is niet gegarandeerd dat de linker ld(1) hem ook vindt.

Gebruik ldconfig(1) voor het leggen van de link libNaam.so.xxx naar het bestand libNaam.so.xxx.yyy.zzz. Deze stap verzekert ons ervan dat de bibliotheek goed in elkaar zit en dat hij wordt herkend als dynamische bibliotheek. Deze stap heeft geen invloed op hoe de programma's worden gelinkt, alleen op hoe de bibliotheek wordt geladen tijdens uitvoering.

Maak een symbolische link van libNaam.so.xxx.yyy.zzz (of van libNaam.so.xxx, de soname) naar libNaam.so, zodat de linker de bibliotheek kan vinden met behulp van de -l optie. Om dit te laten werken moet de naam voldoen aan de syntax libNaam.so.

10. Aanmaken van een statische bibliotheek

Als men daarentegen een statische bibliotheek wil maken (of twee versies om zo ook de mogelijkheid tot statisch linken te kunnen bieden) dan moet als volgt te werk worden gegaan:

Let op: de linker gaat altijd op zoek naar bibliotheken met de naam libNaam.so en daarna naar libNaam.a. Als we de twee bibliotheken dezelfde naam geven dan kunnen we over het algemeen niet onderscheiden welke van de twee wordt gelinkt (de dynamische versie zal over het algemeen als eerste worden gebruikt als hij als eerste wordt gevonden).

Om deze reden is het aan te bevelen om de twee versies van één en dezelfde bibliotheek als volgt te benoemen: libNaam_s.a voor de statische versie en libNaam.so voor de dynamische. Voor het linken van de statische versie wordt het commando dan:

	$ gcc -o programma -lNaam_s
en voor de dynamische versie:
	$ gcc -o programma -lNaam

10.1 Het compileren van de code

Voor het compileren worden geen speciale dingen gebruikt. Analoog aan het vaststellen van de geheugenplaats van de diverse objecten, is het niet noodzakelijk om de optie -f PIC te gebruiken (hoewel dit wel kan).

10.2 Linken van objecten in de bibliotheek

In het statische geval is er geen aparte link-slag. Alle objecten worden gearchiveerd met behulp van het commando ar(1). Om het vinden van symbolen sneller te laten verlopen is het aan te bevelen om ranlib(1) los te laten op de bibliotheek. Hoewel dit laatste niet strikt noodzakelijk is kan het ertoe leiden dat een module niet wordt gevonden tijdens linken. Niet alle indirecte afhankelijkheden worden namelijk opgelost tijdens het aanmaken van de library (de module is bijvoorbeeld nodig bij de aanroep van een andere module die verderop in de bibliotheek staat waardoor meerdere link- slagen nodig zijn voor het oplossen van de referenties).

10.3 Installeren van de bibliotheek

De statische bibliotheek krijgt een naam met de syntax libNaam.a als je alleen een statische bibliotheek wilt. In het geval van twee versies van de bibliotheek beveel ik aan om de syntax libNaam_s.a aan te houden zodat je directe invloed hebt op het kiezen van de dynamische of statische versie.

Deze vorm van linken geeft de mogelijkheid tot het gebruik van de optie -static. Deze optie bepaald of de module /lib/ld-linux.so wordt geladen maar veranderd niet het zoekgedrag van de linker. Aldus kan -static in combinatie met ld(1) een dynamische bibliotheek vinden en daarmee verder gaan (in plaats van verder te zoeken naar het statische alternatief), wat tijdens uitvoering tot fouten kan leiden omdat de code geen onderdeel is van de executeerbare code maar ook de laadmodule niet is meegelinkt.

11. Statisch versus dynamisch linken

Laten we aannemen dat we een programma willen distribueren dat gebruik maakt van een bibliotheek die we alleen statisch mogen distribueren (zoals bijvoorbeeld applicaties met Motif).

Er zijn twee mogelijkheden. De eerste is alleen gebruik te maken van de statische (.a) bibliotheken, waarmee we het gebruik van de dynamische lader omzeilen. Op deze manier aangemaakte programma's hebben tijdens uitvoering helemaal geen bibliotheken nodig (zelfs niet /lib/ld-linux.so). Nadeel is dat alle code in het bestand zit en het programma dus erg groot wordt. Idealiter (de tweede mogelijkheid) willen we dus het programma dynamisch linken, waarmee we de hoeveelheid te distribueren executeerbare code aanzienlijk verkleinen maar daarmee de aanwezigheid van dynamische bibliotheken is vereist om het programma uit te kunnen voeren. Dit kan een probleem zijn (sommige mensen hebben bijvoorbeeld geen Motif op hun systeem).

Er is nog een derde mogelijkheid en dat is een mengeling tussen dynamisch en statisch linken. In dat geval zal men er dus voor kiezen de bibliotheek die mogelijke problemen kan veroorzaken statisch mee te linken en de rest dynamisch. Dit is een ideale vorm voor distributie van software.

Men kan bijvoorbeeld drie verschillende versies van hetzelfde programma als volgt maken:

	$ gcc -static -o programma.static programma.o -lm_s -lXm_s -lXt_s -lX11_s -lXmu_s -lXpm_s

	$ gcc -o programma.dynamic programma.o -lm -lXm -lXt -lX11 -lXmu -lXpm

	$ gcc -o programma.mixed programma.o -lm -lXm_s -lXt -lX11 -lXmu -lXpm
In het laatste geval wordt alleen de Motif bibliotheek Motif (-lXm_s) statisch meegelinkt, de rest dynamisch. De omgeving waarin dit programma wordt uitgevoerd moet de juiste versies van de bibliotheken libm.so.xxx libXt.so.xxx libX11.so.xxx libXmu.so.xxx en libXpm.so.xxx bevatten.

Meer informatie:

Zie de ELF-HOWTO.