Dynamisk länkning och shared objects i FreeBSD: Difference between revisions

From Peters wiki
Jump to navigation Jump to search
mNo edit summary
mNo edit summary
 
(26 intermediate revisions by the same user not shown)
Line 1: Line 1:
Detta ämne är under utveckling.
Problem med dynamisk länkning är ofta det som strular när man ska kompilera länka ihop något större program. Särskilt om man inte är root på systemet och måste installera sina program på andra ställen än systemets standardställen.
Problem med dynamisk länkning är ofta det som strular när man ska kompilera länka ihop något större program. Särskilt om man inte är root på systemet och måste installera sina program på andra ställen än systemets standardställen.


 
== Exempel: myapp & libfoo.so ==
== Bygga och länka mot en so-fil ==


Vi börjar med ett exempel på ett litet program som består av en exekverbar fil + en so-fil. Detta exempel kommer sedan användas för att åskådliggöra problemen runt so-filer och dynamisk länkning. Vår filstruktur i exemplet är:
Vi börjar med ett exempel på ett litet program som består av en exekverbar fil + en so-fil. Detta exempel kommer sedan användas för att åskådliggöra problemen runt so-filer och dynamisk länkning. Vår filstruktur i exemplet är:
Line 15: Line 12:
│   ├── foo.c
│   ├── foo.c
│   └── foo.h
│   └── foo.h
├── main.c
└── main.c
└── myapp
</pre>
</pre>
== Länka utan sökväg till runtime loadern ==


=== Bygga och länka libfoo.so ===
=== Bygga och länka libfoo.so ===
Källkoden till libfoo.so ligger i foo.c och dess interface i foo.h. När vi kompilerar koden måste vi använda -fPIC flaggan. Vi använder det fullständiga kommandot:
{{Box Kommando|Kompilera foo.c|gcc -std=c99 -Wall -Wextra -O2 -fPIC -c foo.c}}


[[Category:FreeBSD]]
Källkoden till libfoo.so ligger i foo.c och dess interface i foo.h. När vi kompilerar koden måste vi använda {{ic|-fPIC}} flaggan. Vi använder det fullständiga kommandot:
{{Cmd|<nowiki>gcc -std=c99 -Wall -Wextra -O2 -fPIC -c foo.c</nowiki>}}
 
För att sedan länka ihop libfoo.so så använder vi följande kommando:
{{Cmd|gcc -shared -o libfoo.so foo.o}}
Optionen {{ic|-shared}} används för att skapa ett shared object (so-fil).
 
=== Bygga och länka myapp ===
 
Vår exekverbar fil består av källkoden main.c men behöver en funktion i libfoo.so för att kunna köras. Vi kompilerar källkoden med kommandot:
 
{{Cmd|<nowiki>gcc -std=c99 -Wall -Wextra -Wno-unused -O2 -Ilib -c main.c</nowiki>}}
 
För att länka programmet myapp kör vi kommandot:
{{Cmd|gcc -Llib -o myapp main.o -lfoo}}
 
Om man nu försöker köra myapp kommer man få följande fel:
{{Cmd|./myapp|output=/libexec/ld-elf.so.1: Shared object "libfoo.so" not found, required by "myapp"}}
När programmet myapp ska laddas hittar loadern inte libfoo.so filen. För att lösa problem kan vi sätta variabeln {{ic|LD_LIBRARY_PATH}} och peka ut var vår libfoo.so ligger. Nu kommer loadern hitta libfoo.so och myapp går att köra. Denna lösning har dock många nackdelar.
 
== Länka med absolut eller relativ sökväg i ELF-filen ==
 
För att slippa behöva sätta {{ic|LD_LIBRARY_PATH}} vid exekvering kan sökvägar byggas in i ELF filen vid länkning. Loadern kommer vid exekvering använda dessa för att hitta de dynamiskt länkade so-filerna. Innan länkning, sätt {{ic|LD_RUN_PATH}} till att innehålla sökvägen till libfoo.so. Sen länka som ovan.
 
Vi kan nu köra myapp utan att behöva sätta {{ic|LD_LIBRARY_PATH}}. Man kan kolla vilka sökvägar som är inbyggda i ELF-filen genom att köra kommandot:
{{Cmd|<nowiki>readelf -Wa ./myapp | grep RPATH</nowiki>|output=0x000000000000000f (RPATH)              Library rpath: [/<path>/lib]}}
 
{{ic|LD_RUN_PATH}} byggs in i ELF-filen, så vid exekvering hittas libfoo.so. Det finns dock ett undantag där {{ic|LD_RUN_PATH}} inte byggs in, och det är om någon annan sökväg anges via -rpath kommandot till länkaren. Då ignoreras {{ic|LD_RUN_PATH}} helt. Så ett bättre sätt är att alltid ange sökvägarna explicit vid länkningen.
 
Bygg och länka libfoo.so som ovan, men länka myapp med följande kommando:
{{Cmd|gcc -Llib -Wl,-rpath,<path>/lib -o myapp main.o -lfoo}}
 
Nackdelen med en absolutsökväg är att libfoo.so måste ligga på samma ställe överallt programmet myapp ska köra. Så en relativ sökväg kan tyckas vara bättre. Då behöver bara myapp och libfoo.so relativt ligga på samma ställe. Låt och ändra till en relativ sökväg genom att länka myapp med kommandot:
{{Cmd|gcc -Llib -Wl,-rpath,./lib -o myapp main.o -lfoo}}
 
Vi kan nu köra myapp, men bara när vi står i samma katalog som myapp själv. Så relativ sökväg löser inte heller problemet att kunna köra myapp från olika ställen.
 
== Länka med sökväg via $ORIGIN i ELF-filen ==
 
För att lösa problemet med relativ sökväg till libfoo.so, så ska vi använda oss av {{ic|$ORIGIN}} i den relativa sökvägen. {{ic|$ORIGIN}} löses upp till den aktuella platsen som myapp ligger vid exekveringen. Tyvärr så är namnet {{ic|$ORIGIN}} illa valt då $ betyder olika saker i Makefiler, skal etc. Om vi vill köra kommandot i ett bash-skal så behöver vi lägga till ett \ före $-tecknet:
{{Cmd|gcc -Llib -Wl,-rpath,\$ORIGIN/lib -Wl,-z,origin -o myapp main.o -lfoo}}
I FreeBSD måste vi också lägga till optionen {{ic|-z origin}} till länkaren för att ange att objektet ev. använder sig av {{ic|$ORIGIN}}.
 
Nu kan myapp och libfoo.so läggas på olika ställen och ändå kunna exekveras. Det enda som krävs är att libfoo.so alltid ligger i en katalog som heter lib under myapp filen.
 
[[Category:Guide]]

Latest revision as of 11:53, 21 April 2013

Problem med dynamisk länkning är ofta det som strular när man ska kompilera länka ihop något större program. Särskilt om man inte är root på systemet och måste installera sina program på andra ställen än systemets standardställen.

Exempel: myapp & libfoo.so

Vi börjar med ett exempel på ett litet program som består av en exekverbar fil + en so-fil. Detta exempel kommer sedan användas för att åskådliggöra problemen runt so-filer och dynamisk länkning. Vår filstruktur i exemplet är:

.
├── Makefile
├── lib
│   ├── Makefile
│   ├── foo.c
│   └── foo.h
└── main.c

Länka utan sökväg till runtime loadern

Bygga och länka libfoo.so

Källkoden till libfoo.so ligger i foo.c och dess interface i foo.h. När vi kompilerar koden måste vi använda -fPIC flaggan. Vi använder det fullständiga kommandot:

user $ gcc -std=c99 -Wall -Wextra -O2 -fPIC -c foo.c

För att sedan länka ihop libfoo.so så använder vi följande kommando:

user $ gcc -shared -o libfoo.so foo.o

Optionen -shared används för att skapa ett shared object (so-fil).

Bygga och länka myapp

Vår exekverbar fil består av källkoden main.c men behöver en funktion i libfoo.so för att kunna köras. Vi kompilerar källkoden med kommandot:

user $ gcc -std=c99 -Wall -Wextra -Wno-unused -O2 -Ilib -c main.c

För att länka programmet myapp kör vi kommandot:

user $ gcc -Llib -o myapp main.o -lfoo

Om man nu försöker köra myapp kommer man få följande fel:

user $ ./myapp
/libexec/ld-elf.so.1: Shared object "libfoo.so" not found, required by "myapp"

När programmet myapp ska laddas hittar loadern inte libfoo.so filen. För att lösa problem kan vi sätta variabeln LD_LIBRARY_PATH och peka ut var vår libfoo.so ligger. Nu kommer loadern hitta libfoo.so och myapp går att köra. Denna lösning har dock många nackdelar.

Länka med absolut eller relativ sökväg i ELF-filen

För att slippa behöva sätta LD_LIBRARY_PATH vid exekvering kan sökvägar byggas in i ELF filen vid länkning. Loadern kommer vid exekvering använda dessa för att hitta de dynamiskt länkade so-filerna. Innan länkning, sätt LD_RUN_PATH till att innehålla sökvägen till libfoo.so. Sen länka som ovan.

Vi kan nu köra myapp utan att behöva sätta LD_LIBRARY_PATH. Man kan kolla vilka sökvägar som är inbyggda i ELF-filen genom att köra kommandot:

user $ readelf -Wa ./myapp | grep RPATH
0x000000000000000f (RPATH) Library rpath: [/<path>/lib]

LD_RUN_PATH byggs in i ELF-filen, så vid exekvering hittas libfoo.so. Det finns dock ett undantag där LD_RUN_PATH inte byggs in, och det är om någon annan sökväg anges via -rpath kommandot till länkaren. Då ignoreras LD_RUN_PATH helt. Så ett bättre sätt är att alltid ange sökvägarna explicit vid länkningen.

Bygg och länka libfoo.so som ovan, men länka myapp med följande kommando:

user $ gcc -Llib -Wl,-rpath,<path>/lib -o myapp main.o -lfoo

Nackdelen med en absolutsökväg är att libfoo.so måste ligga på samma ställe överallt programmet myapp ska köra. Så en relativ sökväg kan tyckas vara bättre. Då behöver bara myapp och libfoo.so relativt ligga på samma ställe. Låt och ändra till en relativ sökväg genom att länka myapp med kommandot:

user $ gcc -Llib -Wl,-rpath,./lib -o myapp main.o -lfoo

Vi kan nu köra myapp, men bara när vi står i samma katalog som myapp själv. Så relativ sökväg löser inte heller problemet att kunna köra myapp från olika ställen.

Länka med sökväg via $ORIGIN i ELF-filen

För att lösa problemet med relativ sökväg till libfoo.so, så ska vi använda oss av $ORIGIN i den relativa sökvägen. $ORIGIN löses upp till den aktuella platsen som myapp ligger vid exekveringen. Tyvärr så är namnet $ORIGIN illa valt då $ betyder olika saker i Makefiler, skal etc. Om vi vill köra kommandot i ett bash-skal så behöver vi lägga till ett \ före $-tecknet:

user $ gcc -Llib -Wl,-rpath,\$ORIGIN/lib -Wl,-z,origin -o myapp main.o -lfoo

I FreeBSD måste vi också lägga till optionen -z origin till länkaren för att ange att objektet ev. använder sig av $ORIGIN.

Nu kan myapp och libfoo.so läggas på olika ställen och ändå kunna exekveras. Det enda som krävs är att libfoo.so alltid ligger i en katalog som heter lib under myapp filen.