3. Megosztott programkönyvtárak

Megosztott programkönyvtárak olyan programkönyvtárak, amiket a program indulásakor tölt be. Ha a megosztott programkönyvtárak megfelelően vannak telepítve, az összes program az indulása után automatikusan új megosztott programkönyvtárat használ. Ez ennél egy kicsit rugalmasabb és bonyolultabb jelenleg, mert a linuxos megoldás megengedi azt is, hogy:

3.1. Konvenciók

A fent leírt tulajdonságok megvalósításához a megosztott programkönyvtáraknak számos konvenciót és irányelvet követniük kell. Fontos megérteni a különbséget a programkönyvtárak nevei között, különösen a "so-név" és a "valódi név" között (és azok kapcsolatát). Azt is meg kell értened, hogy hova kell elhelyezni ezeket a programkönyvtárakat a fájlrendszerben.

3.1.1. Megosztott programkönyvtárak nevei

Minden megosztott programkönyvtárnak van egy speciális neve, amit "so-név"-nek hívnak. Az so-névnek van egy "lib" előtagja, ezt követi a programkönyvtár neve, majd egy ".so" tag majd egy pont, és a verziószám (egy speciális kivétel az alacsony szintű C programkönyvtárak, amik nem kezdődnek "lib"-el). A verziószám akkor növekedik, ha az interfész változik. A "teljes so-név" tartalmazza előtagként a könyvtár (directory) nevét, amiben a programkönyvtár található. Egy működő rendszeren a teljes so-név egyszerűen egy szimbolikus hivatkozás a megosztott programkönyvtár valódi nevére.

Minden megosztott programkönyvtárnak van "valódi neve" is, ami annak az fájlnak a neve, ami a jelenlegi programkönyvtár kódját tartalmazza. A valódi név az so-névhez ad egy pontot, a minor számot, egy újabb pont-ot és a kibocsátási számot (release number). Az utolsó pont és a kibocsátási szám opcionális. A minor szám és a kibocsátási szám arra való, hogy tudd, pontosan melyik változata van az adott programkönyvtárnak telepítve. Megjegyezzük, hogy ezek a számok nem feltétlenül egyeznek meg azzal, amilyen verziószámmal a programkönyvtár dokumentációjában hivatkoznak, habár az megkönnyítené a dolgokat.

Van továbbá egy név, amit a fordításnál használunk, amikor a programkönyvtárra hivatkozunk (ezt "csatolási név"-nek fogjuk hívni). Ez egyszerűen a so-név verzió nélkül.

A megosztott programkönyvtárak használatának kulcsa a neveik szétválasztásában rejlik. A programokban a szükséges megosztott programkönyvtárak so-neveire van csak szükség. Ennek megfelelően, amikor egy megosztott programkönyvtárat készítesz, mindössze egy programkönyvtárat készítesz egy speciális fájlnéven (részletes verzió információkkal). Amikor telepíted az új változatát a programkönyvtárnak, akkor a megfelelő könyvtárak egyikébe kell elhelyezned és az ldconfig(8) programot kell futtatnod. Az ldconfig megvizsgálja a létező fájlokat és elkészíti a so-neveket, mint szimbolikus hivatkozásokat a valódi nevekre, ezzel együtt beállítja az /etc/ld.so.cache gyorstár-fájlt is.

Az ldconfig nem állítja be a szerkesztési neveket, általában ez megtörtént már a programkönyvtár telepítése során, a szerkesztési név egyszerűen egy szimbolikus hivatkozás a "legújabb" so-névre vagy a legújabb valódi névre. Az tanácsolnám legyen a csatolási név egy szimbolikus hivatkozás az so-névre, mivel a legtöbb esetben a frissített programkönyvtárat szeretnéd automatikusan használni, mikor csatolod azt a programodhoz. Megkérdeztem H. J,. Lu-t, miért nem állítja be automatikusan az ldconfig a szerkesztési neveket. A magyarázata alapvetően az volt, hogy talán a programkönyvtár legutóbbi verzióját szeretnéd futtatni a kóddal, de az is lehet, hogy egy fejlesztői változatra szeretnél hivatkozást egy régi - talán inkompatibilis - programkönyvtár helyett. Ezért az ldconfig nem kísérli meg kitalálni, hogy mit akarsz a programhoz csatolni, így a telepítőnek kell meghatározni azt, és módosítani a szimbolikus hivatkozást arra a programkönyvtárra, amit a szerkesztő használni fog.

Így az /usr/lib/libreadline.so.3 egy teljes so-név, amit az ldconfig készített, például mint szimbolikus hivatkozást a /usr/lib/libreadline.so.3.0 fájlra. Kellene lennie egy /usr/lib/libreadline.so szerkesztési névnek is, ami egy szimbolikus hivatkozás lehet a /usr/lib/libreadline.so.3 fájlra.

3.1.2. Elhelyezés a fájlrendszerben

A megosztott programkönyvtárakat el kell helyezni valahol a fájlrendszerben. A legtöbb nyílt forrású szoftver a GNU szabványok (standards) követésére törekszik. További információt az info:standards#Directory_Variables info dokumentumban találhatsz. A GNU szabványok azt ajánlják, hogy alapértelmezésben minden programkönyvtárat az /usr/local/lib könyvtárba telepítsünk a forráskód közzétételekor (és minden parancsnak az /usr/local/bin könyvtárba kellene kerülnie). Ezek a szabványok konvenciókat is meghatároznak arra vonatkozólag, hogyan bíráljuk felül ezt az alapértelmezett beállítást a telepítési eljárás során.

A Filesystem Hierarchy Standard (FHS) meghatározza mi hol legyen egy terjesztésben (lásd: http://www.pathname.com/fhs). Az FHS szerint a legtöbb programkönyvtárat az /usr/lib könyvtárba kell telepíteni, de azokat amik az elinduláshoz szükségesek a /lib könyvtárban kellene lenniük, és azok a könyvtárak, melyek nem részei a rendszernek azokat kellene a /usr/local/lib könyvtárba rakni.

Nincs igazán ellentmondás a két dokumentum között. A GNU szabványok a fejlesztőknek, míg az FHS a terjesztést összeállítóknak (akik szelektíven felülbírálják a forrás alapértelmezett beállításait, rendszerint a rendszer csomagkezelőjével) szóló ajánlások. Gyakorlatilag ez szépen működik: a "legújabb" (valószínűleg hibás!) forrást, amit letöltesz, automatikusan a "local" könyvtárba (/usr/local) telepíti magát, azoknál a kódoknál, amik "megértek" a csomag kezelők magától érthetődben felülbírálják az alapértelmezett beállításokat, és elhelyezik a kódot az alapértelmezett helyére a terjesztésben. Megjegyezzük, hogy ha a programkönyvtárad meghív olyan programokat, amik csak a programkönyvtárak által hívhatóak, akkor ezeket a programokat a /usr/local/libexec (ami /usr/libexec a Linux terjesztésekben) kell elhelyezni. A Red Hat alapú rendszerek egy sajátossága, hogy az /usr/local/lib könyvtár nincs az alapértelmezett programkönyvtár-keresési útvonalban (lásd a megjegyzéseket az /etc/ld.so.conf fájlról lejjebb). Egy másik szokványos programkönyvtár-hely az /usr/X11R6/lib, ahova az X-winodows rendszerrel kapcsolatos programkönyvtárakat szokás elhelyezni. Megjegyezzük, hogy a /lib/security a PAM modulok által használt hely, de ezek rendszerint DL programkönyvtárak (lásd lejjebb).

3.2. Hogyan használjuk a programkönyvtárakat?

GNU glibc alapú rendszereken - ide tartozik az összes Linux rendszer - egy ELF bináris futtatható fájl elindítása automatikusan magával vonja a programbetöltő elindítását. Linux rendszereken ennek a betöltőnek a neve /lib/ld-linux.so.X (ahol X a verzió számot jelöli). Ez a betöltő keresi meg és tölti be az összes program által használt megosztott programkönyvtárat.

Azoknak az elérési utaknak a listája, amiben a betöltő a programkönyvtárakat keresi, az /etc/ld.so.conf fájlban található. Több Red Hat alapú terjesztésben a /usr/local/lib nem található meg ebben a fájlban. Ezt én hibának tekintem és az /usr/local/lib hozzáadást az /etc/ld.so.conf fájlhoz egy általános "javításnak" gondolom, ami minden Red Hat alapú rendszeren szükséges lehet.

Ha felül akarsz bírálni néhány eljárást egy programkönyvtárban, de meg akarod tartani az eredetit, akkor elhelyezheted a felülbírálandó programkönyvtárak nevét (.o fájlok) az /etc/ld.so.preload fájlban. Ezek az "előtöltött" programkönyvtárak elsőbbséget élveznek a standard programkönyvtárakkal szemben. Ez az előtöltő fájl általában átmenti foltozásra szolgál, és a terjesztések rendszerint nem tartalmaznak ilyeneket.

Nem igazán hatékony ezeket az elérési utakat megkeresni minden programindításakor. Ezért egy gyorstárazó módszert alkalmazunk. Az ldconfig(8) program alapértelmezésben az /etc/ld.co.conf fájlt olvasva beállítja a megfelelő szimbolikus hivatkozásokat a dinamikus hivatkozások könyvtáraiban (dynamic link directories) (így azok a konvenciókat fogják követni), és egy gyorstár-fájlt készít /etc/ld.so.cache néven. Ezt a gyorstárat használja aztán a többi program. Ez nagyon felgyorsítja a programkönyvtárak elérését. A következmény az, hogy az ldconfig parancsot minden esetben futtatni kell, ha új DLL-at adunk a rendszerhez vagy ha eltávolítjuk azt, vagy mikor átállítjuk a DLL programkönyvtárakat. Az ldconfig futtatás a leggyakoribb feladat, amit egy csomagkezelőnek el kell végeznie, mikor programkönyvtárat telepít. Indulásnál a dinamikus betöltő az /etc/ld.so.cache fájlt használja és utána tölti be a szükséges programkönyvtárakat.

Megjegyezzük, hogy a FreeBSD teljesen más fájlnevet használ ennek a gyorstárnak. FreeBSD-ben az ELF gyorstár a /var/run/ld-elf.so.hints, míg az a.out gyorstár a /var/run/ld.so.hints fájl. Az ldconfig(8) ezeket is frissíti, így ez az elnevezésbeli különbség csak néhány egzotikus esetben érdekes.

3.3. Környezeti változók

Számos környezeti változóval szabályozható az előbb bemutatott működés. Ezek a környezeti változók lehetővé teszik, hogy beavatkozzunk a programkönyvtárak betöltésének és használatának menetébe.

3.3.1. LD_LIBRARY_PATH

Átmenetileg helyettesíthetsz néhány programkönyvtárat bizonyos programok futtatásakor. Linuxon a LD_LIBRARY_PATH környezeti változó egy kettősponttal elválasztott listája azoknak az elérési utaknak, ahol a programkönyvtárakat keresi, a szokásos helyek előtt. Ez hasznos hibakereséskor, ha egy új vagy nem szokványos programkönyvtárat használunk valamilyen speciális célból. Az LD_PRELOAD környezeti változó egy kettősponttal elválasztott listája azoknak a megosztott programkönyvtáraknak, amelyek függvényei felüldefiniálják a standard programkönyvtárakét, mint ahogy azt az /etc/ld.so.preload teszi. Meg kell említenünk, hogy a LD_LIBRARY_PATH használható majdnem minden Unix-szerű rendszeren, de nem mindegyiken. Például ez a funkció elérhető HP-UX-on, de a környezeti változó neve SHLIB_PATH. AIX-on ezt a funkciót a LIBPATH nevű változóval érhetjük el (ugyanezzel a szintaxissal, kettősponttal elválasztott lista).

LD_LIBRARY_PATH kényelmes fejlesztésnél vagy tesztelésnél, de szükségtelen módosítania egy normál felhasználónak vagy akár egy telepítési eljárásnak normál esetben. A miértekről a "Why LD_LIBRARY_PATH is Bad" című írásban, a http://www.visi.com/~barr/ldpath.html honlapon olvashatsz. Ezek ellenére használható fejlesztéskor vagy teszteléskor, vagy ha egy probléma behatárolása nem megy másképpen. Ha nem akarod beállítani a LD_LIBRARY_PATH környezeti változót, akkor Linuxon közvetlenül futtathatod a programbetöltőt, megfelelő argumentumokkal. Az alábbi példában a PATH-ot fogja használni az LD_LIBRARY_PATH környezeti változó tartalma helyett, így futtatja a megadott fájlt.
  /lib/ld-linux.so.2 --library-path PATH EXECUTABLE
Az ld-linux.so argumentum nélkül futtatva további információkat add arról, hogyan használhatod, de még egyszer hangsúlyozom, hogy normál körülmények között ne használd ezt, kizárólag hibakeresésre.

3.3.3. További környezeti változók

Számos más környezeti változó is van, ami a betöltési eljárást szabályozza. Ezek LD_ vagy RTLD_ előtagokkal kezdődnek. A legtöbb alacsony szintű hibakeresést tesz lehetővé a betöltési eljárásban, vagy speciális tulajdonságok megvalósítását teszik lehetővé. A legtöbb nem túl jól dokumentált. Ha szükséged van rájuk legjobb, ha elolvasod a betöltő forráskódját, ha többet akarsz megtudni ezekről a környezeti változókról.

A felhasználói beavatkozás engedélyezése a dinamikusan csatolt programkönyvtárak betöltésénél katasztrofális eredményre vezethet, setuid/setgid-es programok esetén. Ezért a GNU betöltőben (ami betölti a program többi részét annak indulásakor), ha a program setuid vagy setgid beállításokkal rendelkezik, akkor az eddig említett és a többi hasonló környezeti változó vagy figyelmen kívül marad, vagy hatása erősen korlátozva lesz. Ha az uid és az euid, vagy ha a gid és a egid különbözik, a betöltő feltételezi, hogy a program setuid vagy setgid-es (vagy annak gyermeke), ezért jelentősen csökkenti a csatolás kontrollálásának lehetőségét környezeti változókkal. Ha a GNU glibc programkönyvtár forráskódját olvasod, láthatod ezt, különösen az elf/rtlf.c és a sysdeps/generic/dl-sysdep.c fájlokat olvasva. Ez azt jelenti, ha eléred, hogy a uid és a euid, valamit a gid és egid megegyezzenek ezek a változók kifejtik teljes hatásukat, mikor a programot futtatod. Más Unix-szerű rendszerek hasonló okból kifolyólag, de máshogy kezelik ezt a problémát: a setuid és setgid-es programot indokolatlanul nem befolyásolhatjuk környezeti változókkal.

3.4. Megosztott programkönyvtárak készítése

Megosztott programkönyvtárat készíteni könnyű. Először egy tárgykód-fájlt kell készítenünk, amit a megosztott programkönyvtárba fogunk pakolni, a gcc -fPIC vagy -fpic kapcsolóinak segítségével. A -fPIC és -fpic opciók engedélyezik a "pozíció független kód" (position independent code) generálását, ami szükséges a megosztott programkönyvtárak készítéséhez (a különbségeket lásd alább). Az so-nevet a -Wl kapcsolóval adhatod meg a gcc-nek. A -Wl opció a szerkesztőnek (linker) szól (ebben az esetben a -soname linker opció) - a vesszők a -Wl után nem helyesírási hibák, és nem szabad szóközöket tenni közéjük. A megosztott programkönyvtár készítéséhez az alábbi formátumot használd:

gcc -shared -Wl,-soname,your_soname \
    -o library_name file_list library_list

Íme egy példa, ami két tárgykód-fájlt készít (a.o és b.o) aztán ezekből egy megosztott programkönyvtárat. Megjegyezzük, hogy ez a fordítás (compile) tartalmazza a hibakeresési (debug) információkat és a figyelmeztetések (warnings) generálását is, amik nem feltétlenül szükségesek a megosztott programkönyvtárak készítéséhez. A fordítás tárgykód-fájlokat állít elő (-c használata), és tartalmazza a szükséges -fPIC kapcsolót:

gcc -fPIC -g -c -Wall a.c
gcc -fPIC -g -c -Wall b.c
gcc -shared -Wl,-soname,libmystuff.so.1 \
    -o libmystuff.so.1.0.1 a.o b.o -lc

Íme néhány jó tanács:

Fejlesztés közben az egyik tipikus probléma annak a programkönyvtárnak a módosítása, amit más programok is használnak -- és nem szeretnéd, hogy a többi program is a "fejlesztési" programkönyvtárat használja, csak egy kiszemelt alkalmazást szeretnél tesztelni vele. Az ld "rpath" szerkesztési (link) opcióját kell használnod ebben az esetben, ami szerkesztési időben meghatározza a futásidejű programkönyvtár (runtime library) keresési útvonalát egy adott programra. A gcc-t az rpath opcióval az alábbi módon használhatod:
 -Wl,-rpath,$(DEFAULT_LIB_INSTALL_PATH)
Ha ezt az opciót használod amikor elkészíted (build) a programkönyvtár kliens programját, akkor nem kell azzal vacakolnod, hogy a LD_LIBRARY_PATH-al elkerüld a összeütközéseket vagy, hogy más módszerekkel elrejtsd a programkönyvtárat.

3.5. Megosztott programkönyvtárak telepítése és használata

Ha már készítettél megosztott programkönyvtárat, nyilván használni is akarod. Az egyik lehetőség, hogy egyszerűen a szabványos könyvtárak egyikébe másolod a programkönyvtárat (pl. /usr/lib) és lefuttatod a ldconfig(8)-ot.

Először is készítened kell egy megosztott programkönyvtárat valahol. Aztán be kell állítanod a szükséges szimbolikus hivatkozásokat (symbolic links), különösen a so-névről a valódi névre (akárcsak a verziómentes so-névről mutatót - ami a so-név ami ".so"-val végződik - azoknak a felhasználóknak, akik nem határoznak meg verziót egyáltalán). Az egyszerűbb megoldás, hogy az alábbi parancsot futtatod:
 ldconfig -n directory_with_shared_libraries

Végül, amikor fordítod a programodat, meg kell mondanod a szerkesztőnek azokat a statikus és megosztott programkönyvtárakat, amiket használni akarsz. Erre használd a -l és -L kapcsolókat.

Ha nem tudod, vagy nem akarod telepíteni a programkönyvtáradat egy standard helyre (például nincs jogod módosítani az /usr/lib könyvtárat), akkor más megoldást kell választanod. Ebben az esetben telepítened kell a programkönyvtárat valahova, aztán elég információt kell adnod a programodnak, hogy megtalálja a programkönyvtárat. Többféle mód is létezik erre. Használhatod a gcc -L kapcsolóját egyszerűbb esetekben. Használhatod a "rpath"-os megoldást (lásd feljebb), különösen, ha csak egy speciális program használja a programkönyvtárat, ami a "nem-standard" helyen van. Használhatsz környezeti változókat is, hogy kézben tartsd a dolgokat. Az LD_LIBRARY_PATH környezeti változó használhatod, ami egy kettősponttal elválasztott listája azoknak az elérési utaknak, ahol a megosztott programkönyvtárakat keressük, mielőtt a szokásos helyeken próbálkoznánk. Ha bash-t használsz indíthatod a my_program-ot így:

LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH  my_program

Ha csak néhány kiválasztott függvényt akarsz módosítani, akkor megteheted, hogy készítesz egy módosító tárgykód-fájlt, és beállítod a LD_PRELOAD környezeti változót. A függvények ebben a tárgykódban csak azokat a függvényeket fogják felülírni, amik a programkönyvtárban szerepelnek (a többit nem változtatja meg).

Rendszerint nyugodtan frissítheted a programkönyvtárakat, ha API változás volt a programkönyvtár készítője feltételezhetően megváltoztatta a so-nevet. Ezért lehet több programkönyvtár egyszerre egy rendszeren, a megfelelő lesz kiválasztva mindegyik programhoz. Ha a program mégis összeomlik, a programkönyvtár frissítésének hatására, ráveheted, hogy használja a régebbi programkönyvtár-verziót. Ehhez másold a régi programkönyvtárat vissza a rendszerre valahova. Változtasd meg a program nevét (legyen a régi név ".orig" kiterjesztéssel), majd készíts egy kis indító-szkriptet, ami visszaállítja a régi programkönyvtárat a programnak. A régi programkönyvtárat elhelyezheted egy saját speciális helyre, ha akarod, használhatod a számozási konvenciót, hogy több verzió is lehessen ugyanabban a könyvtárban. Íme egy minta indító-szkript:
  #!/bin/sh
  export LD_LIBRARY_PATH=/usr/local/my_lib:$LD_LIBRARY_PATH
  exec /usr/bin/my_program.orig $*
Kérlek ne használd ezt, amikor a saját programodat készíted. Próbálj meggyőződni arról, hogy a programkönyvtáraid vagy visszamenőlegesen kompatibilisek, vagy növelted a verzió számot a so-névben minden esetben, ha nem kompatibilis változást végeztél. Ez csak egy vészmegoldás a legrosszabb esetekre.

Egy program által használt megosztott programkönyvtárak listáját az ldd(1) programmal kaphatod meg. Tehát például, ha az ls program által használt megosztott programkönyvtárakat szeretnéd látni, írd a következőt:
  ldd /bin/ls
Megkapod azoknak a so-nevek listáját, amiktől a program függ, a könyvtárral (directory) együtt, amiben azok a nevek feloldhatóak. Gyakorlatilag minden esetben legalább két függőséged lesz:

Vigyázat: ne futtasd az ldd-t olyan programra, amiben nem bízol. Ahogy az világosan le van írva az ldd(1) kézikönyvében, az ldd úgy működik (a legtöbb esetben), hogy beállít egy speciális környezeti változót (ELF tárgykódok esetén az LD_TRACE_LOADED_OBJECTS-et) és futtatja a programot. Lehetséges egy megbízhatatlan program számára, hogy rávegye az ldd felhasználót mesterséges kód futtatására (ahelyett, hogy egyszerűen megmutatná az ldd információt). Tehát a biztonság kedvéért ne használd az ldd-t olyan programra, amiben nem bízol meg.

3.6. Inkompatibilis programkönyvtárak

Abban az esetben, ha egy programkönyvtár új változata binárisan inkompatibilis a régivel, akkor a so-nevet meg kell változtatni. C-ben négy alapvető oka lehet annak, hogy egy programkönyvtár inkompatibilissé válik:

  1. A függvény viselkedése megváltozik, nem egyezik meg az eredeti specifikációval.

  2. Nyilvános adatelemek megváltoztak (kivétel: hozzáadott opcionális elemek a struktúrák végén nem okoznak problémát, ha ezeket a struktúrákat csak a programkönyvtár foglalja le)

  3. Nyilvános függvényt eltávolítottak.

  4. A nyilvános függvény interfésze megváltozott.

Ha ezeket el tudod kerülni, akkor a programkönyvtáraid binárisan kompatibilisek lesznek. Más szóval az alkalmazás bináris interfésze (Application Binary Interface - ABI) kompatibilis maradhat, ha a fenti típusú változtatásokat elkerülöd. Például, hozz létre új függvényeket, de ne töröld a régieket. Hozzáadhatsz új elemeket a struktúrákhoz, de csak akkor, ha meggyőződtél róla, hogy a régi programok nem lesznek érzékenyek erre a változásra. Csak a struktúra végére adj új elemeket, és csak a programkönyvtár (és nem az alkalmazás) által elfoglalt struktúrákban teheted ezt meg. Az új elemek legyenek opcionálisak (vagy a programkönyvtár töltse ki őket) stb. Figyelem: valószínűleg nem tudod kiterjeszteni a struktúráidat, ha a felhasználók tömbökben használják azokat.

C++ (illetve egyéb fordítási időben használatos sablonokat (template) vagy "compiled dispatched" eljárásokat támogató nyelvek) esetén a helyzet kicsit trükkösebb. A fenti megkötéseket mind figyelembe kell venni, és meg sok minden mást is. Ennek oka, hogy néhány információ rejtve (under the covers) került megvalósításra a lefordított kódban. Ennek eredménye nem nyilvánvaló, ha nem tudod, hogy tipikusan, hogy szokták a C++-t megvalósítani. Szigorúan véve ezek nem "új" dolgok, csak arról van szó, hogy C++ használhat adatstruktúrákat úgy, hogy az a meglepetés erejével hathat. Az alábbi egy - a Troll Tech's Technical FAQ alapján összeállított (feltehetőleg hiányos) listája azoknak a dolgoknak, amit nem tehetsz meg C++-ban, ha meg akarod tartani a kompatibilitást.

  1. virtuális függvények újra megvalósításának hozzáadása, (hacsak nem vagy benne biztos, hogy a régi programok az eredeti megvalósítást fogják használni). Mert a fordító már fordítási időben (nem csatolási (link) időben) kiértékeli a SuperClass::virtualFunction() függvényhívást.

  2. virtuális tagfüggvény hozzáadása vagy törlése, mert ez megváltoztathatja a minden alosztály vtbl-jének méretét és szerkezetét.

  3. bármilyen olyan adattag típusának megváltoztatása vagy törlése, amit inline tagfüggvények érnek el.

  4. osztályhierarchia megváltoztatása, kivéve az új levelek hozzáadását.

  5. privát adattag hozzáadása vagy elvétele, mert ez megváltoztatja a méretét és szerkezetét minden alosztálynak.

  6. public vagy protected tagsági függvények eltávolítása, hacsak nem inline típusúak.

  7. public vagy protected tagfüggvény inline típusúvá tenni.

  8. módosítani a inline típusú függvények működését, kivéve ha régi változat továbbra is működik.

  9. a tagsági függvények hozzáférési jogának (public, protected vagy private) megváltoztatása egy hordozható programban, mert néhány fordító hozzáadja a hozzáférési jogot a függvénynévhez.

Ez a hosszú lista is jól mutatja, hogy a C++ programkönyvtár fejlesztőknek bizonyos esetekben sokkal jobban meg kell tervezniük a dolgokat, hogy megtartsák a bináris kompatibilitást. Szerencsére Unix-szerű rendszereken (a Linuxot is beleértve) egy programkönyvtárad több verziója lehet egyszerre betöltve. Így amíg van elég lemezterület, a felhasználók futtathatnak "régi" programokat, amik régi programkönyvtárakat használnak.