5. Egyéb

5.1. nm utasítás

Az nm(1) utasítás megmutatja milyen szimbólumok találhatóak egy adott programkönyvtárban. Mind statikus, mind megosztott programkönyvtárakra működik. Egy adott programkönyvtárra az nm(1) a szimbólumok neveit, és minden szimbólum típusát és értékét képes megmutatni. Azt is képes meghatározni, hogy a forráskódban hol volt a szimbólum definiálva (fájlnév és sor), ha ezt az információt tartalmazza a programkönyvtár (lásd a -l kapcsolót).

A szimbólum típusok kicsit több magyarázatot igényelnek. A típust egy karakter jelzi. A kisbetű azt jelenti, hogy a szimbólum lokális, míg a nagybetű azt, hogy globális (külső). A tipikus szimbólum típusok a következők: T (normál definíció a kód részben), D (inicializált adat szekció), B (nem inicializált adat szekció), U (nem definiált; a szimbólumot használja a programkönyvtár de nincs definiálva abban), és W (waek; ha más programkönyvtár szintén definiálja ezt a szimbólumot, és az a definíció elrejti azt).

Ha tudod a függvény nevét, de nem emlékszel, hogy melyik programkönyvtárban van definiálva, akkor a nm "-o" kapcsolóját (ami minden sor elé odarakja az fájlnevet) használhatod a grep-el együtt, hogy megtaláld a programkönyvtár nevét. Bourne héjban kereshetsz minden programkönyvtárban a /lib, /usr/lib, /usr/local/lib és azok alkönyvtáraiban a "cos"-ra az alábbiak szerint:
nm -o /lib/* /usr/lib/* /usr/lib/*/* \
      /usr/local/lib/* 2> /dev/null | grep 'cos$' 

Sokkal több információt található az nm-ről a "info" dokumentumában (info:binutils#nm).

5.2. Programkönyvtár konstruktor és destruktor függvények

A programkönyvtárak megvalósíthatnak nyilvános inicializációs és cleanup függvényeket, a gcc __attribute__((constructor)) és __attribute__((destructor)) függvény attribútumait használva. További információt a gcc info oldalain találsz erről. A konstruktor eljárások a dlopen visszatérése előtt (vagy a main() előtt, ha a programkönyvtár betöltési időben kerül megnyitásra) hívódnak meg. A destruktor eljárások a dlclose visszatérése előtt (vagy a exit() vagy a main() befejezésekor, ha a programkönyvtár betöltési időben került megnyitásra) hívódnak meg. A C prototípusa ezeknek a függvényeknek a következő:
  void __attribute__ ((constructor)) my_init(void);
  void __attribute__ ((destructor)) my_fini(void);

A megosztott programkönyvtárakat tilos a gcc "-nostartfiles" vagy "-nostdlib" kapcsolóival fordítani. Ha ezeket használjuk a konstruktor/destruktor eljárások nem fognak lefutni (hacsak valami speciális nem történik).

5.2.1. Speciális függvények, _init és _fini (ELAVULT/VESZÉLYES)

Történelmileg létezik két speciális függvény a _init és a _fini, amik lehetővé teszik a konstruktorok és a destruktor vezérlését. Ugyanakkor ezek elavultak és használatuk megjósolhatatlan viselkedést eredményezhet. A programkönyvtáraidban szükségtelen ezeket használnod. Helyettük a fenti függvény argumentumokkal ellátott konstruktor és destruktor függvényeket használd.

Ha régi rendszeren kell dolgoznod, vagy olyan kóddal ami használja a _init vagy a _fini függvényeket, akkor itt található a működési leírásuk. Két speciális függvényt definiáltak a modulok inicializálására és a befejezésre: _init és a _fini. Ha a programkönyvtárban nyilvános "_init" függvény van definiálva, akkor az meghívódik, amikor a programkönyvtárat először megnyitjuk (dlopen()-el vagy egyszerűen megosztott programkönyvtárként). C programban ez egyszerűen azt jelenti, hogy definiálsz egy függvényt _init névvel. Létezik egy _fini nevű függvény, ami meghívódik mindannyiszor, ha a program befejezi a programkönyvtár használatát. (dlclose() meghívásával, amikor az a referencia számlót 0-ra állítja vagy a program normális kilépésekor). A C prototípusok:
  void _init(void);
  void _fini(void);

Ebben az esetben, ha az tárgykódot készítünk (".o") gcc-vel a "-nostartfiles"opciót meg kell adnunk. Ez megóvja a C fordítót attól, hogy a .so fájljal szemben a rendszerindítási programkönyvtárakat satolja a programhoz. Ellenkező esetben "multiple-definition" (többszörös definíció) hibát fogsz kapni. Megjegyezünk, hogy ez teljesen eltér attól az esettől, amikor modulokat a javasolt függvényattribútumos megoldással fordítasz. Köszönet Jim Mischel-nek és Tim Gentry-nek a javaslatárt, hogy kerüljön ide egy leírás a _init és _fini-ről, és hogy segítettek elkészíteni azt.

5.3. Megosztott programkönyvtárak, mint szkriptek

Jó dolog, hogy a GNU betöltő engedélyezi, hogy a programkönyvtárak szöveges fájlok is lehetnek, amiket egy speciális szkript nyelven kell írni a szokásos programkönyvtár-formátum helyett. Ez hasznos például programkönyvtárak közvetett összekapcsolása esetén. Példaként álljon itt a rendszerem /usr/lib/libc.so fájlja:
/* GNU ld script
   Use the shared library, but some functions are only in
   the static library, so try that secondarily.  */
GROUP ( /lib/libc.so.6 /usr/lib/libc_nonshared.a )

További információt találsz erről a ld texinfo dokumentációjában a linker scripts szekcióban (ld parancs nyelv). Általános információ a info:ld#Options and info:ld#Commands, fejezetben. Az utasítások pedig a info:ld#Option Commands-ban.

5.4. Szimbólum verziók és verzió szkriptek

A külső függvényekre történő hivatkozások jellemzően egy szükségszerű bázishoz vannak kötve, amelyek közül nem mindegyik kötődik a külső függvényhez, az alkalmazás indulásakor. (Typically references to external functions are bound on an as-needed basis, and are not all bound when the application starts up.) Ha a megosztott programkönyvtár régi, az igényelt interfésze hiányozhat, amikor az alkalmazás megpróbálja használni azt. Ez azonnali és váratlan hibát okozhat.

A probléma megoldható a szimbólumok verzióval való megjelölésével, és a verzió-szkript csatolásával. Szimbólum verziók használatával a felhasználó figyelmeztetést kap, ha olyan programot indít el, amely túl régi programkönyvtárat akar használni. Többet tudhatsz meg az ld kézkönyvből, a http://www.gnu.org/manual/ld-2.9.1/html_node/ld_25.html honlapon.

5.5. GNU libtool

Ha olyan alkalmazást készítesz amit több rendszerre szeretnél használni, akkor javasolt a GNU libtool használata a programkönyvtár fordításához és telepítéséhez. A GNU libtool egy általános programkönyvtár-készítést támogató szkript. A Libtool elrejti a megosztott programkönyvtárak bonyolultságát egy konzisztens és hordozható interfész mögé. A Libtool hordozható interfészt biztosít a tárgykód fájlok készítéséhez, a programkönyvtárak (statikus és megosztott), a futtatható fájlokhoz csatolásához, a futtatható fájlok hibakereséséhez, a programkönyvtárak és futtatható fájlok telepítéséhez. Tartalmaz továbbá egy libltdl programkönyvtárat, ami egy hordozható csatolófelület a dinamikusan betölthető programkönyvtárakhoz. Többet tudhatsz meg a http://www.gnu.org/software/libtool/manual.html honlapon található dokumentációból.

5.6. Szimbólumok eltávolítása

A generált fájlokban található szimbólumok hasznosak hibakeresésnél, de sok helyet foglalnak. Ha helyre van szükséged, akkor csökkentheted a szimbólumok számát.

A legjobb eljárás először a tárgykódokat szimbólumokkal generálni és a tesztelést és hibakaresést ezekkel végezni, sokkal könnyebb így. Azután, mikor a program már alaposan tesztelve van a strip(1)-et használhatod a szimbólumok eltávolítására. A strip(1) utasítás sok lehetőséget ad arra, hogy meghatározd milyen szimbólumokat akarsz eltávolítani. További részletekért olvasd a dokumentációt.

Egy másik lehetőség a GNU ld "-S" és "-s" kapcsolóinak használata. Az "-S"'mellőzi a hibakereséshez szükséges szimbólumokat (de nem az összes szimbólumot). A "-s" pedig az összes szimbólum információt kihagyja a kimeneti fájlból. Használhatod ezeket a kapcsolókat a gcc-n keresztül is a "-Wl,-S" és "-Wl,-s" formában. Ha sosem akarsz szimbólumokat, akkor ezek a kapcsolók megfelelőek, használd őket. De ez a kevésbé rugalmas megoldás.

5.7. Nagyon kicsi futtatható fájlok

Egy igen hasznos leírást találhatsz a Whirlwind Tutorial on Creating Really Teensy ELF Executables for Linux honlapon. Ez leírja, hogyan készíthetsz igazán parányi futtatható fájlokat. Őszintén szólva a legtöbb ott található trükk nem használható normál esetben, de jól mutatják, hogy működik az ELF igazából.

5.8. C++ vs. C

Semmi akadálya annak, hogy C++ programból C programkönyvtári függvényeket hívj meg. A C++ kódban a C függvény extern "C"-nek kell definiálni. Ellenkező esetben a szerkesztő (linker) nem fogja megtalálni a C függvényt. A C++ fordító "szétszedi" a C++ függvények neveit (például típusazonosítás céljából), jelezni kell neki, hogy egy adott függvényt, mint C függvényt kell hívnia (és így ezt a szétszedést nem kell használni).

Ha olyan programkönyvtárat írsz amit C és C++-ból is hívni kell, akkor ajánlott a megfelelő header fájlba elhelyezd a 'extern "C"'utasítást azért, hogy ezt a tulajdonságot automatikusan biztosítsd a felhasználóknak. Ha ezt kombinálod a szokásos #ifndef-el a fájl elején, hogy elkerüld a header fájl újrafeldolgozását, akkor ez azt jelenti, hogy egy tipikus header fájl, ami C és C++-ban is használható (neve legyen mondjuk foobar.h) így néz ki:
/* Explain here what foobar does */

#ifndef FOOBAR_H
#define FOOBAR_H

#ifdef __cplusplus
extern "C" {
#endif

 ... header code for foobar goes here ...

#ifdef  __cplusplus
}
#endif
#endif

5.9. A C++ inicializáció felgyorsítása

A KDE fejlesztők jelezték, hogy nagy grafikus C++ alkalmazások indulása sokáig tart. Ez részben a sok újra allokációnak köszönhető. Létezik néhány megoldás a problémára. További információt Waldo Bastian: Making C++ ready for the desktop című írásában olvashatsz.

5.10. Linux Standard Base (LSB)

A Linux Standard Base (LSB) projekt célja, hogy olyan szabványokat dolgozzon ki és népszerűsítsen, amelyek növelik a kompatibilitást a Linux terjesztések között, és lehetővé teszik az alkalmazások futtatását minden ennek megfelelő Linux rendszeren. A projekt honlapja a http://www.linuxbase.org webhelyen érhető el.

Egy szép cikk jelent meg George Kraft IV (IBM Linux Technology Center senior szoftvermérnök) tollából 2002 októberében arról, hogyan fejlesszünk LSB kompatibilis alkalmazásokat: Developing LSB-certified applications: Five steps to binary-compatible Linux applications. Természetesen a kódot úgy kell megírni, hogy egy standardizált réteget használjon, ha azt akarod, hogy a program hordozható legyen. Továbbá a LSB eszközöket biztosít a C/C++ az alkalmazás készítőknek, a programok LSB kompatibilitásának ellenőrzésére. Ezek az eszközök kihasználják a szerkesztő (linker) néhány tulajdonságát, és néhány speciális programkönyvtárat használnak a szükséges ellenőrzéseknek elvégzésére. Nyilvánvalóan telepítened kell ezeket az eszközöket, ha el akarod végezni ezeket az ellenőrzéseket. Az LSB honlapján megtalálhatók. Ezt követően egyszerűen a "lsbcc" kell használnod, mint C/C++ fordítót (az lsbcc belsőleg készít egy csatolási környezetet, ami jelezni fogja, ha bizonyos LSB szabályok sérülnek):
 $ CC=lsbcc make myapplication
  (or)
 $ CC=lsbcc ./configure; make myapplication 
Az lsbappchk programot arra használhatod, hogy ellenőrizd a program valóban csak az LSB által standardizált függvényeket használja:
 $ lsbappchk myapplication
Az LSB csomagolási útmutatót is követned kell (pl. használj RPM v3-at, használj LSB által meghatározott csomagneveket, és az add-on szoftvereket az /opt-ba kell telepítened alapértelmezetten). További információkat a cikkben és az LSB honlapján találsz.