HTML

Hackstock

Hack the planet! Hackers unite!

Címkék

Címkefelhő

Dinamikus linkelés (2) - importok betöltése

2010.01.02. 23:57 :: theshadow

Eddig a Dinamikus Linker fő programágát vizsgáltuk, de adós maradtam még néhány nagyobb alprogram működésének magyarázatával.

Most hadd törlesszek ebből azzal, hogy az ImageLoader link metódusának mélységeiben nézek körül.

A linkelés folyamata az ImageLoader link metódusának hívásával indul el.

this->recursiveLoadLibraries(context,loaderRPaths);

Itt történik a modulok betöltése egymást után.

// get list of libraries this image needs

fLibrariesCount = this->doGetDependentLibraryCount();

fLibraries = new DependentLibrary[fLibrariesCount];

bzero(fLibraries, sizeof(DependentLibrary)*fLibrariesCount);

DependentLibraryInfo libraryInfos[fLibrariesCount]; 

this->doGetDependentLibraries(libraryInfos);

Itt összegyüjtjük azoknak a könyvtáraknak az infóblokkjait, amelyeket be kell töltenünk. Ezeket a Dylib, Weak Dylib, Reexport Dylib töltőparancsokból gyüjtünk össze (doGetDependentLibraries).

requiredLib.image = context.loadLibrary(requiredLibInfo.name, true, depNamespace, this->getPath(), &thisRPaths);


Az importok feldolgozása során első lépés a betöltés. Vegyük észre, hogy a dyld.cpp forrásban láttuk, hogy a contex struktúra loadLibrary elemét a libraryLocator rutin címével inicializáltuk, amely ugyancsak a load függvényt hívja meg (nem az ImageLoad vagy ImageLoaderMachO osztályból!).

Érdekes, hogy az ellenőrzések során nem probléma, ha az importált könyvtárak ellenörző összege nem egyezik meg, csak egy flag jelzi ezt. Ezzel ellentétben a betöltött könyvtár verziószámának meg kell egyezni vagy magasabbnak kell lenni, mint ami az import információs blokkjában tárolt verziószám, máskülönben kivétel generálódik.

// tell each to load its dependents

for(unsigned int i=0; i < fLibrariesCount; ++i){

DependentLibrary& lib = fLibraries[i];

if ( lib.image != NULL ) {

lib.image->recursiveLoadLibraries(context, thisRPaths);

}

}

Természetesen sikeres betöltés után az ő szükséges importjaikat is be kell tölteni, ezeknek az image-eknek is meghívjuk a recursiveLoadLibraries metódusát.

Ezek után az a kérdés, hogy a dyld.cpp-ben található libraryLocator és azon belüle a load metódus milyen módon tölti be az importált könyvtárakat.

// try all path permutations and check against existing loaded images

ImageLoader* image = loadPhase0(path, context, NULL);

if ( image != NULL )

return image;

 

// try all path permutations and try open() until first successs

std::vector<const char*> exceptions;

image = loadPhase0(path, context, &exceptions);

if ( image != NULL )

return image;

A realpath eljárással a szimbólikus linkek kerülnek feloldásra mielőtt bármit is tennénk, ez után kerülhet csak sor a betöltés megkísérlésére, ami a loadPhase0 segítségével történik meg.

// handle DYLD_ROOT_PATH which forces absolute paths to use a new root

if ( (sEnv.DYLD_ROOT_PATH != NULL) && (path[0] == '/') ) {

for(const char* const* rootPath = sEnv.DYLD_ROOT_PATH ; *rootPath != NULL; ++rootPath) {

char newPath[strlen(*rootPath) + strlen(path)+2];

strcpy(newPath, *rootPath);

strcat(newPath, path);

ImageLoader* image = loadPhase1(newPath, context, exceptions);

if ( image != NULL )

return image;

}

}

 

// try raw path

return loadPhase1(path, context, exceptions);

Ha nem volt DYLD_ROOT_PATH környezeti változó meghatározva korábban és az útvonalleírás nem '/' karakterrel kezdődik - tehát relatív útvonalról van szó, akkor azt kell megállapítani, hogy mihez képest relatív. Az első kísérlet ebben az esetben, hogy a DYLD gyökérkönyvtárakhoz viszonyítva keresünk file-t. Ha egyikben sem találtuk vagy nem is határoztunk meg ilyen változót, akkor a jelenlegi munkakönyvtár (CWD) lesz a bázisa.

// handle LD_LIBRARY_PATH environment variables that force searching

if ( context.useLdLibraryPath && (sEnv.LD_LIBRARY_PATH != NULL) ) {

image = loadPhase2(path, context, NULL, sEnv.LD_LIBRARY_PATH, exceptions);

if ( image != NULL )

return image;

}

 

// handle DYLD_ environment variables that force searching

if ( context.useSearchPaths && ((sEnv.DYLD_FRAMEWORK_PATH != NULL) || (sEnv.DYLD_LIBRARY_PATH != NULL)) ) {

image = loadPhase2(path, context, sEnv.DYLD_FRAMEWORK_PATH, sEnv.DYLD_LIBRARY_PATH, exceptions);

if ( image != NULL )

return image;

}

// try raw path

image = loadPhase3(path, context, exceptions);

if ( image != NULL )

return image;

// try fallback paths during second time (will open file)

if ( !context.dontLoad  && (exceptions != NULL) && ((sEnv.DYLD_FALLBACK_FRAMEWORK_PATH != NULL) || (sEnv.DYLD_FALLBACK_LIBRARY_PATH != NULL)) ) {

image = loadPhase2(path, context, sEnv.DYLD_FALLBACK_FRAMEWORK_PATH, sEnv.DYLD_FALLBACK_LIBRARY_PATH, exceptions);

if ( image != NULL )

return image;

}

Az 1. fázisban még megkísérlünk érvényesíteni néhány környezeti változó beállítást, ha jelen vannak. Ilyen a LD_LIBRARY_PATH, a DYLD_FRAMEWORK_PATH és a DYLD_LIBRARY_PATH változók tartalma; ezek mindegyikével a 2. fázis próbálkozik. Ha nem jártunk volta sikerrel még mindig vagy talán nem is léteznek ezek a beállítások, akkor végső esetként a tényleges relatív útvonal marad.

ImageLoader* image = NULL;

const char* frameworkPartialPath = getFrameworkPartialPath(path);

if ( frameworkPaths != NULL ) {

if ( frameworkPartialPath != NULL ) {

const int frameworkPartialPathLen = strlen(frameworkPartialPath);

for(const char* const* fp = frameworkPaths; *fp != NULL; ++fp) {

char npath[strlen(*fp)+frameworkPartialPathLen+8];

strcpy(npath, *fp);

strcat(npath, "/");

strcat(npath, frameworkPartialPath);

//dyld::log("dyld: fallback framework path used: %s() -> loadPhase4(\"%s\", ...)\n", __func__, npath);

image = loadPhase4(npath, context, exceptions);

if ( image != NULL )

return image;

}

}

}

if ( libraryPaths != NULL ) {

const char* libraryLeafName = getLibraryLeafName(path);

const int libraryLeafNameLen = strlen(libraryLeafName);

for(const char* const* lp = libraryPaths; *lp != NULL; ++lp) {

char libpath[strlen(*lp)+libraryLeafNameLen+8];

strcpy(libpath, *lp);

strcat(libpath, "/");

strcat(libpath, libraryLeafName);

//dyld::log("dyld: fallback library path used: %s() -> loadPhase4(\"%s\", ...)\n", __func__, libpath);

image = loadPhase4(libpath, context, exceptions);

if ( image != NULL )

return image;

}

}

Ha a 2. fázis hívása során framework útvonalakat határoztunk meg, akkor a betöltendő modul nevéből megállapítjuk a framework nevét (".framework/") és ezt próbáljuk meg kombinálni a könyvtárlista elemeiven, a loadPhase4 segítségével próbáljuk ezeket betöltetni. Import könyvtár esetén az első '/' karakter utáni részt próbálja kombinálni a könyvtárakat tartalmazó lista elemeivel és ugyanúgy a loadPhase4-gyel kíséreljük meg a betöltést.

Az előzőekben említett loadPhase3 felelős az import útvonalakban alkalmazható változó értékek kifejtéséért. Ilyen az útvonal leírás elején elhelyezhető "@executable_path", amely helyére a futtatható állományunk könyvtára kerül. A "@loader_path" helyére a dinamikus betöltő modul könyvtára kerül, az "@rpath" változó értéket pedig az RPath lánc segítségével helyettesítjük és próbáljuk meg feloldani. Ha még mindig nem jártunk sikerrel, akkor Legvégül akkor az "@rpath"-et megkjséreljük 2. fázis és a LD_LIBRARY_PATH környezeti változó segítségével kifejteni.

Az import könyvtárnevek suffix behelyettesítése a loadPhase4-ben történik. Képzeljük el, hogy a DYLD_IMAGE_SUFFIX környezeti változónak adtunk egy értéket és ez az érték íródik minden importálandó könyvtár nevének végére a ".dylib" végződés elé. Ezen a módon lehetséges debug és más egyéb fajta könyvtárverziókat is tartani ugyanabban a folderben és ezzel a beállítással térhetünk át a használatukra. Egyébként ha ez a kísérlet nem jár sikerrel, természetesen megpróbálkozunk a suffix behelyettesítése nélküli betöltéssel is, hiszen az eredeti import file még ott lehet. A kísérletek során a loadPhase5-t hívjuk.

Az 5. fázis az utolsó előttinek tekinthető, hiszen az exceptions paraméter jelenléte dönti el, hogy megnyitni próáljuk az import könyvtárat vagy az előzőleg betöltöttek között keresünk (ha már korábban betöltöttük, akkor nem töltük be ismét).

A már betöltött állományok közötti keresést a loadPhase5check eljárás végzi el. A forráskódban figyeljük meg a hashszám alkalmazását - ezzel spórolunk meg több strcmp hívást, ezzel is gyorsítva a keresést. Találat esetén természetesen az sAllImages vektorban tárolt referenciát adjuk vissza eredményként.

loadPhase5open hívása esetén file megnyitásával töltünk be import könyvtárat. A findLoadedImage még tesz egy kísérletet az sAllImages tömbbel való találatra, mert lehet, hogy a file átnevezésre került vagy szimbólikus linken keresztül töltődött be előzőleg. Végső lépésben a loadPhase6 metódust hívjuk.

Ebben már tényleg az előzőekhez hasonló módon beolvassuk a file első 4KB-ját, megvizsgáljuk, hogy Fat állománnyal van dolgunk; ha igen, akkor a megfelelő implementációt keressük meg benne és azt az ImageLoaderMachO konstruktorával töltetjük be.

Megfelelő ellenőrzések után az újonan betöltött Mach-O file referenciája is belekerül az sAllImages vektorba.

Szólj hozzá!

Címkék: macosx

A bejegyzés trackback címe:

http://hackstock.blog.hu/api/trackback/id/tr251640846

Kommentek:

A hozzászólások a vonatkozó jogszabályok  értelmében felhasználói tartalomnak minősülnek, értük a szolgáltatás technikai  üzemeltetője semmilyen felelősséget nem vállal, azokat nem ellenőrzi. Kifogás esetén forduljon a blog szerkesztőjéhez. Részletek a  Felhasználási feltételekben.

Nincsenek hozzászólások.