Introduktion till libfiler



Introduktion till hur man bygger statiska och dynamiska libfiler

Introduktion till hur man bygger statiska och dynamiska libfiler

Copyright Kjell enblom, 2007 GNU Free Documentation License Version 1.1 or later.

Kort introduktion till libfiler
Observera att beskrivningen nedan kan skilja sig något åt mellan olika
Unixdialekter. Beskrivningen nedan ska dock fungera i GNU/Linux.

Biblioteksfiler finns i två former, statiska och dynamiska
(dynamiska kallas även delade/shared).

För statiska bibliotek så bakas delar av biblioteket in i programmet och ligger fast i
programmet. Om det finns ett statiskt bibliotek som heter m och
programmen foo och bar behöver m då ser man till att baka in m (delar av m) i
både foo och bar. Det gör att programfilerna blir större men de
är samtidigt oberoende av biblioteksfilen med biblioteket m i för att
kunna köra.

För att installera och köra foo eller bar på ett system så behövs
bara foo respektive bar. Biblioteket m behövs inte installeras
eftersom de nödvändiga delarna av den finns inbakad i binärerna.

Om m innehåller funktionerna sin och cos och foo är beroende av sin
och bar är beroende av cos så kommer foo att länka med endast sin
och bar att endast länka med cos om de ligger i olika objektfiler.
Programmen länkar alltså endast med de objektfiler för de funktioner
i de statiska biblioteket som de behöver.
Ett statiskt bibliotek är egentligen ett arkiv med objektfiler, där varje
objektfil kan innehålla en eller flera funktioner och symboler.

Statiska biblioteksfiler heter typisk libfoo.a där foo är namnet
på biblioteket.



Dynamiska/delade bibliotek bakas inte in i programmen.
Programfilerna blir därmed mindre än med statiska bibliotek men
de blir beroende av biblioteksfiler. Om programmen foo och bar
behöver biblioteket m så kommer systemet att ladda in programfilen
och biblioteksfilen libm.so.6 in i minnet. Finessen är att biblioteksfilen
endast behövs laddas in en gång. Programmen foo och bar delar på
samma biblioteksfil.

Man ska dock vara medveten om att hela biblioteksfilen
laddas in i minnet så om biblioteksfilen innehåller många funktioner
kommer alla funktioner att laddas in även om det bara är några få som
används.

Biblioteksfilerna laddas in dynamiskt när de behövs och de delas
mellan flera program. Därav dynamiska delade bibliotek (shared libraries).
Dynamiska biblioteksfiler har typiskt namn på formen libfoo.so.x.y
eller libfoo.so.x.y.z där x är version (huvudversion) och y eller y.z är underversion och patch-release.
Filerna kan även ha namn på formen libfoo-x.y.so.
libzvt.so.2.2.10 är ett exempel på den första formen och
libutil-2.4.so är ett exempel på den senare formen.

Om man vid kompilering av ett program med gcc/g++ vill länka med ett
bibliotek så görs detta med flaggan -l
(lilla L). Det gäller både för statiska och dynamiska bibliotek. Om
ett bibliotek finns både som statiskt och dynamiskt kommer i första
hand det dynamiska att användas.

Exempel för att länka med mattefunktioner som finns i biblioteket m:
Kod: Markera allt
gcc program.c -lm -o programfil


biblioteksnamn
Namnet på ett statiskt bibliotek är det som stå mellan lib och
.a. I exemplet med libfoo.a blir det foo.
Namnet på ett dynamiskt bibliotek är det som står efter lib med .so
och huvudversions, underversion och patchrelease borttaget. Om biblioteksfilen heter
libfoo.so.1.2 eller libfoo-1.2.so så är namnet på biblioteket foo.

Länkning
Om man vill att länkaren ska söka i en viss katalog så anges detta med
-Lsökväg. T.ex. -L. eller -L/usr/local/foo/lib .


Det går även att ge sökvägen plus filnamn till ett bibliotek till
länkaren. exempel:
Kod: Markera allt
gcc program.c /usr/lib/libm.a -o programfil


Man kan tvinga länkaren att bygga en statiskt länkad binär med -static. Som standard länkas program dynamiskt.
Kod: Markera allt
gcc foo.c -Wall -static -o foo



Skapa statiska bibliotek
Statiska bibliotek har namn på formen libfoo.a .
För att skapa ett statiskt bibliotek gör man enligt följande:

Kompilera källkodsfilerna med flaggorna -g och -c,
Kod: Markera allt
gcc -g -c fil1.c fil2.c fil3.c


Skapa biblioteksfilen med hjälp av programmet ar.
Kod: Markera allt
ar crv libfoo.a fil1.o fil2.o fil3.o


Skapa därefter en innehållsförteckning med ranlib. Detta steg är
nödvändigt i en del system men inte i Linux men det skadar inte att
göra det.
Kod: Markera allt
ranlib libfoo.a


Nu kan man lista vilka objektfiler som ingår i libfoo.a med:
Kod: Markera allt
ar t libfoo.a


För att lista alla globala symboler i biblioteket kan man använda nm. Då får man bland annat se alla globala funktioner som finns i biblioteket.
Kod: Markera allt
nm -g libfoo.a



Dynamiska/delade bibliotek

Huvudversion, underversion och patchrelease
När man gör större uppdateringar av ett bibliotek, där ändringarna inte är bakåtkompatibla, då ser man till att uppdatera huvudversionen. Det kan t.ex. vara att ta bort funktionalitet, ändra på gränssnitt etc. Dessa ändringar gör att program länkade mot en äldre huvudversion av biblioteket inte kan använda en nyare huvudversion. För det krävs eventuella ändringar i programmet och omkompilering och omlänkning av programmet.

Under-versioner och patchreleaser däremot uppdaterar man vid mindre ändringar, t.ex. buggfixar och utökningar som t.ex. kompatibla tillägg av funktionalitet som inte påverkar den existerande funktionaliteten. Ett program som är länkat mot en huvudversion ska kunna använda nya underversioner och patchreleaser utan omkompilering eller omlänkning. Så om programmet är länkat mot libfoo.so.1 kan det fungera med libfoo.so.1.0, libfoo.so.1.1, libfoo.so.1.2 och så vidare.



Skapa dynamiska/delade bibliotek

Om man har källkodsfilen foo.c och vill skapa biblioteksfilen
libfoo.so.1 så kan det göras på följande sätt:

Kompilera källkoden med:
Kod: Markera allt
gcc -fPIC -Wall -ansi -g -c fil.c


Skapa biblioteksfilen libfoo.so.1.0 som ska innehålla libfoo.so.1. Det görs med följande kommandorad:
Kod: Markera allt
gcc -g -shared -Wl,--soname,libfoo.so.1 -o libfoo.so.1.0 fil.o -lc



Information om libfilen kan man få fram med objdump. Exempel:
Kod: Markera allt
objdump -p libfoo.so.1.0

Med det kan man se vad SONAME är satt till, vilket i exemplet ovan bör vara
libfoo.so.1 . När man länkar ett program vill man länka mot huvudversion. Då kan programmet fungera även mot underversioner (mindre uppdateringar) och patchreleaser av biblioteket.


Även här man lista alla globala symboler i biblioteket med nm. Då får man bland annat se alla globala funktioner som finns i biblioteket.
Kod: Markera allt
nm -g libfoo.so.1.0



Nu är det bara att lägga in biblioteksfilen i en katalog som hittas av
systemet eller lägga det i en katalog, t.ex. /usr/local/mittprogram/lib
och stoppa in sökvägen /usr/local/mittprogram/lib i /etc/ld.so.conf
och därefter köra ldconfig. Programmet ldconfig kommer att sätta upp
symlänken libfoo.so.1 som pekar på libfoo.so.1.0 . Det går även att
köra ldconfig endast för katalogen med biblioteksfilen.
Därefter kan det vara bra att skapa en symlänk libfoo.so som pekar på
libfoo.so.1 där libfoo.so.1 är en länk till biblioteksfilen libfoo.so.1.y (där y är en underversion). Den symboliska länken libfoo.so behövs endast vid länkning i samband med kompilering. Länken libfoo.so.1 -> libfoo.so.1.y används vid körning. Se bild nedan, där libfoo.so.1.2 är biblioteksfilen.



Här sätter vi upp länken libfoo.so.1 -> libfoo.so.1.2 med kommandot ldconfig och sedan länken libfoo.so -> libfoo.so.1 med kommandot ln.
Kod: Markera allt
cp libfoo.so.1.2 /usr/local/mittprogram/lib/
cd /usr/local/mittprogram/lib
/sbin/ldconfig -v -n .
ln -sf libfoo.so.1 libfoo.so



Program som länkas mot libfoo.so.1 kommer att kunna använda
libfoo.so.1.0 som innehåller libfoo.so.1. Om man vid ett senare
tillfälle gör mindre uppdateringar i källkoden och kompilerar om den
på samma sätt som ovan men till filen libfoo.so.1.1 så kommer
programmen att kunna använda den nya filen eftersom den innehåller
libfoo.so.1 som är det bibliotek som programmen förväntar sig att hitta.

Principen brukar vara att vid mindre uppdateringar så har man samma
soname, ovan libfoo.so.1, och uppdaterar underversionen på libfilerna.
Vid större inkompatibla ändringar ger man ett nytt soname med en
uppdaterad versionssiffra vilket med exemplet ovan skulle ge
libfoo.so.2 och filen libfoo.so.2.0 (libfoo.so.2.x).

Genom att göra på det här sättet så kan gamla program fortsätta att
använda libfoo.so.1 (i filerna libfoo.so.1.x) och nya program kan
använda libfoo.so.2 (i filerna libfoo.so.2.x).

Om du tänker använda libtools versionssystem bör du se infosidorna
till libtool, specifikt "Updating version info".
Kod: Markera allt
info libtool "Updating version info"



Vid start av ett program kommer /lib/ld-linux.so.2 att sköta om så
att de dynamiska libfiler som programmet behöver laddas in. Alla
beroenden slås upp och laddas in vilket gör att det kan ta tid att
starta ett program.

För att ta reda på vilka bibliotek ett program behöver för att
kunna köra kan man använda programmet ldd.

ldd programfil

Exempel:
Kod: Markera allt
[kjell-e@dumburk c]$ ldd a.out
        linux-gate.so.1 =>  (0xb7fbe000)
        libc.so.6 => /lib/libc.so.6 (0x009f0000)
        /lib/ld-linux.so.2 (0x009d3000)




Ett litet exempel på ett enkelt bibliotek och ett litet program som
använder det.
Kod: Markera allt
[kjell-e@dumburk c]$ cat  mitt.c
int mitt(int a) {
    return a * a;
}

[kjell-e@dumburk c]$ cat  mittest.c
#include<stdio.h>


int main() {
  printf("%d\n",mitt(17));
  return 0;
}


[kjell-e@dumburk c]$ gcc -fPIC -Wall -ansi -g -c mitt.c

[kjell-e@dumburk c]$ gcc -g -shared -Wl,--soname,libmitt.so.1 -o libmitt.so.1.0 mitt.o -lc


[kjell-e@dumburk c]$ /sbin/ldconfig -v -n .
.:
        libmitt.so.1 -> libmitt.so.1.0 (changed)

[kjell-e@dumburk c]$ ln -sf libmitt.so.1 libmitt.so

[kjell-e@dumburk c]$ gcc mittest.c -L.  -lmitt -o mittest

[kjell-e@dumburk c]$ ./mittest
289




Därefter är det dags att installera mittest i /usr/local/bin och
libmitt i /usr/local/mitt/lib och se till att systemet hittar biblioteksfilerna.
Kod: Markera allt
[root@dumburk c]# mv mittest /usr/local/bin

[root@dumburk c]# echo "/usr/local/mitt/lib" >> /etc/ld.so.conf

[root@dumburk c]# cp libmitt.so.1.0 /usr/local/mitt/lib/

[root@dumburk c]# ldconfig

[root@dumburk c]# ls -l /usr/local/mitt/lib/
totalt 8
lrwxrwxrwx 1 root root   14  1 apr 00.00 libmitt.so.1 -> libmitt.so.1.0
-rwxr-xr-x 1 root root 5177 31 mar 23.59 libmitt.so.1.0

[root@dumburk c]# ln -sf /usr/local/mitt/lib/libmitt.so.1  /usr/local/mitt/lib/libmitt.so

[root@dumburk c]# ls -l /usr/local/mitt/lib/
totalt 8
lrwxrwxrwx 1 root root   34  1 apr 00.01 libmitt.so -> /usr/local/mitt/lib/libmitt.so.1
lrwxrwxrwx 1 root root   14  1 apr 00.00 libmitt.so.1 -> libmitt.so.1.0
-rwxr-xr-x 1 root root 5177 31 mar 23.59 libmitt.so.1.0

[root@dumburk c]# ldd /usr/local/bin/mittest
        linux-gate.so.1 =>  (0x00258000)
        libmitt.so.1 => /usr/local/mitt/lib/libmitt.so.1 (0x002ff000)
        libc.so.6 => /lib/libc.so.6 (0x009f0000)
        /lib/ld-linux.so.2 (0x009d3000)


Om ldd säger "libmitt.so.1 => not found" så beror det på att
biblioteksfilen inte kan hittas. Kontrollera i sådana fall att
katalogen som libmitt ligger i finns med i /etc/ld.so.conf eller i en
av filerna i katalogen /etc/ld.so.conf.d/ . Kör därefter ldconfig. Alternativt sätt rpath. Se avsnittet om länkaren i introduktion till kompilatorn gcc.


Man kan också vilja ha en headerfil mitt.h för biblioteksfunktionerna.
Kod: Markera allt
[kjell-e@dumburk c]$ cat mitt.h
#ifndef MITT_H
#define MITT_H
int mitt(int);
#endif





Se även "How To Write Shared Libraries" som även finns på
http://people.redhat.com/drepper/dsohowto.pdf
Användarvisningsbild
kjell-e
 




Copyright © 2010-2020 Kjell Enblom.
This document is covered by the GNU Free Documentation License, Version 1.3

Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License".