/* $Id: kLdrDyld.c 2837 2006-10-28 03:59:21Z bird $ */ /** @file * * kLdr - The Dynamic Loader. * * Copyright (c) 2006 knut st. osmundsen * * * This file is part of kLdr. * * kLdr is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * kLdr is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with kLdr; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ /******************************************************************************* * Header Files * *******************************************************************************/ #include #include "kLdrHlp.h" #include "kLdrInternal.h" /******************************************************************************* * Defined Constants And Macros * *******************************************************************************/ /** @def KLDRDYLD_STRICT * Define KLDRDYLD_STRICT to enabled strict checks in kLdrDyld. */ #define KLDRDYLD_STRICT 1 /** @def KLDRDYLD_ASSERT * Assert that an expression is true when KLDRDYLD_STRICT is defined. */ #ifdef KLDRDYLD_STRICT # define KLDRDYLD_ASSERT(expr) kldrHlpAssert(expr) #else # define KLDRDYLD_ASSERT(expr) do {} while (0) #endif /******************************************************************************* * Global Variables * *******************************************************************************/ /** Pointer to the head module (the executable). * (This is exported, so no prefix.) */ PKLDRDYLDMOD kLdrDyldHead = NULL; /** Pointer to the tail module. * (This is exported, so no prefix.) */ PKLDRDYLDMOD kLdrDyldTail = NULL; /** Pointer to the head module of the termination order list. */ PKLDRDYLDMOD g_pkLdrDyldTermHead; /** Pointer to the tail module of the termination order list. */ PKLDRDYLDMOD g_pkLdrDyldTermTail; /** Pointer to the head module of the bind order list. * The modules in this list makes up the global namespace used when binding symbol unix fashion. */ PKLDRDYLDMOD g_pkLdrDyldBindHead; /** Pointer to the tail module of the bind order list. */ PKLDRDYLDMOD g_pkLdrDyldBindTail; /** Stack of modules involved in the active loads. * * The main purpose is to allow module init routines to loading and unloading * modules without upsetting the init order and to assist in dereferencing * modules on load failure. * * Each call to kLdrDyldLoad and kLdrDyldLoadExe will start a load frame * when doing a fresh load. All dependant modules will be pushed on each * reference. The frame is completed when all the dependant modules has * been resolved (or a failure occurs). After doing fixups, the frame is * used to do module initialization. Should an error occur during the load * the frame will be used to dereference all the modules involved in the * load operation (it will not however, be used for termination calls as * they are postponed till the last load operation completes). * * Should any of the init calls load a module, a new frame will be created * for that operation and processed before the init call returns to the * previous frame. */ static PPKLDRDYLDMOD g_papStackMods; /** The number of used entries in the g_papStackMods array. */ static uint32_t g_cStackMods; /** The number of entries allocated for the g_papStackMods array. */ static uint32_t g_cStackModsAllocated; /** Number of active load calls. */ static uint32_t g_cActiveLoadCalls; /** Number of active unload calls. */ static uint32_t g_cActiveUnloadCalls; /** Boolean flag indicating that GC is active. */ static uint32_t g_fActiveGC; /** The global error buffer. */ char g_szkLdrDyldError[1024]; /** The Library search path. */ char kLdrDyldLibraryPath[4096]; /** The executable flags. */ uint32_t kLdrDyldFlags; /******************************************************************************* * Internal Functions * *******************************************************************************/ static int kldrDyldDoLoad(const char *pszDll, const char *pszDefPrefix, const char *pszDefSuffix, KLDRDYLDSEARCH enmSearch, unsigned fFlags, PPKLDRDYLDMOD ppMod, char *pszErr, size_t cchErr); static int kldrDyldDoUnload(PKLDRDYLDMOD pMod); static int kldrDyldDoFindByName(const char *pszDll, const char *pszDefPrefix, const char *pszDefSuffix, KLDRDYLDSEARCH enmSearch, unsigned fFlags, PPKLDRDYLDMOD ppMod); static int kldrDyldDoFindByAddress(uintptr_t Address, PPKLDRDYLDMOD ppMod, uint32_t *piSegment, uintptr_t *poffSegment); static int kldrDyldDoGetName(PKLDRDYLDMOD pMod, char *pszName, size_t cchName); static int kldrDyldDoGetFilename(PKLDRDYLDMOD pMod, char *pszFilename, size_t cchFilename); static int kldrDyldDoQuerySymbol(PKLDRDYLDMOD pMod, uint32_t uSymbolOrdinal, const char *pszSymbolName, uintptr_t *pValue, uint32_t *pfKind); static void kldrDyldDoModuleTerminationAndGarabageCollection(void); static uint32_t kldrDyldStackNewFrame(void); int kldrDyldStackPushModule(PKLDRDYLDMOD pMod); static int kldrDyldStackFrameCompleted(void); static void kldrDyldStackDropFrame(uint32_t iStackTop, uint32_t iStackBottom, int rc); static int kldrDyldCopyError(int rc, char *pszErr, size_t cchErr); /** * Initialize the dynamic loader. */ int kldrDyInit(void) { kLdrDyldHead = kLdrDyldTail = NULL; g_pkLdrDyldTermHead = g_pkLdrDyldTermTail = NULL; g_pkLdrDyldBindHead = g_pkLdrDyldBindTail = NULL; kLdrDyldFlags = 0; g_szkLdrDyldError[0] = '\0'; return 0; } /** * Terminate the dynamic loader. */ void kldrDyTerm(void) { } /** * Loads a module into the current process. * * @returns 0 on success, non-zero native OS status code or kLdr status code on failure. * @param pszDll The name of the dll to open. * @param pszDefPrefix Prefix to use when searching. * @param pszDefSuffix Suffix to use when searching. * @param enmSearch Method to use when locating the module and any modules it may depend on. * @param fFlags Flags, a combintation of the KLDRYDLD_LOAD_FLAGS_* \#defines. * @param phMod Where to store the handle to the loaded module. * @param pszErr Where to store extended error information. (optional) * @param cchErr The size of the buffer pointed to by pszErr. */ int kLdrDyldLoad(const char *pszDll, const char *pszDefPrefix, const char *pszDefSuffix, KLDRDYLDSEARCH enmSearch, unsigned fFlags, PHKLDRMOD phMod, char *pszErr, size_t cchErr) { int rc; /* validate arguments and initialize return values. */ if (pszErr && cchErr) *pszErr = '\0'; *phMod = NIL_HKLDRMOD; KLDRHLP_VALIDATE_STRING(pszDll); KLDRHLP_VALIDATE_OPTIONAL_STRING(pszDefPrefix); KLDRHLP_VALIDATE_OPTIONAL_STRING(pszDefSuffix); KLDRHLP_VALIDATE_ENUM(enmSearch, KLDRDYLD_SEARCH); KLDRHLP_VALIDATE_OPTIONAL_BUFFER(pszErr, cchErr); /* get the semaphore and do the job. */ rc = kldrHlpSemRequest(); if (!rc) { PKLDRDYLDMOD pMod = NULL; g_cActiveLoadCalls++; rc = kldrDyldDoLoad(pszDll, pszDefPrefix, pszDefSuffix, enmSearch, fFlags, phMod, pszErr, cchErr); g_cActiveLoadCalls--; kldrDyldDoModuleTerminationAndGarabageCollection(); kldrHlpSemRelease(); *phMod = pMod; } return rc; } /** * Unloads a module loaded by kLdrDyldLoad. * * @returns 0 on success, non-zero native OS status code or kLdr status code on failure. * @param hMod Module handle. */ int kLdrDyldUnload(HKLDRMOD hMod) { int rc; /* validate */ KLDRDYLD_VALIDATE_HKLDRMOD(hMod); /* get sem & do work */ rc = kldrHlpSemRequest(); if (!rc) { g_cActiveUnloadCalls++; rc = kldrDyldDoUnload(hMod); g_cActiveUnloadCalls--; kldrDyldDoModuleTerminationAndGarabageCollection(); kldrHlpSemRelease(); } return rc; } /** * Finds a module by name or filename. * * This call does not increase any reference counters and must not be * paired with kLdrDyldUnload() like kLdrDyldLoad(). * * @returns 0 on success. * @returns KLDR_ERR_MODULE_NOT_FOUND or some I/O error on failure. * @param pszDll The name of the dll to look for. * @param pszDefPrefix Prefix than can be used when searching. * @param pszDefSuffix Suffix than can be used when searching. * @param enmSearch Method to use when locating the module. * @param fFlags Flags, a combintation of the KLDRYDLD_LOAD_FLAGS_* \#defines. * @param phMod Where to store the handle of the module on success. */ int kLdrDyldFindByName(const char *pszDll, const char *pszDefPrefix, const char *pszDefSuffix, KLDRDYLDSEARCH enmSearch, unsigned fFlags, PHKLDRMOD phMod) { int rc; /* validate & initialize */ *phMod = NIL_HKLDRMOD; KLDRHLP_VALIDATE_STRING(pszDll); /* get sem & do work */ rc = kldrHlpSemRequest(); if (!rc) { PKLDRDYLDMOD pMod = NULL; rc = kldrDyldDoFindByName(pszDll, pszDefPrefix, pszDefSuffix, enmSearch, fFlags, phMod); kldrHlpSemRelease(); *phMod = pMod; } return rc; } /** * Finds a module by address. * * This call does not increase any reference counters and must not be * paired with kLdrDyldUnload() like kLdrDyldLoad(). * * @returns 0 on success. * @returns KLDR_ERR_MODULE_NOT_FOUND on failure. * @param Address The address believed to be within some module. * @param phMod Where to store the module handle on success. * @param piSegment Where to store the segment number. (optional) * @param poffSegment Where to store the offset into the segment. (optional) */ int kLdrDyldFindByAddress(uintptr_t Address, PHKLDRMOD phMod, uint32_t *piSegment, uintptr_t *poffSegment) { int rc; /* validate & initialize */ *phMod = NIL_HKLDRMOD; if (piSegment) *piSegment = ~(uint32_t)0; if (poffSegment) *poffSegment = ~(uintptr_t)0; /* get sem & do work */ rc = kldrHlpSemRequest(); if (!rc) { PKLDRDYLDMOD pMod = NULL; rc = kldrDyldDoFindByAddress(Address, &pMod, piSegment, poffSegment); kldrHlpSemRelease(); *phMod = pMod; } return rc; } /** * Gets the module name. * * @returns 0 on success and pszName filled with the name. * @returns KLDR_ERR_INVALID_HANDLE or KLDR_ERR_BUFFER_OVERFLOW on failure. * @param hMod The module handle. * @param pszName Where to put the name. * @param cchName The size of the name buffer. * @see kLdrDyldGetFilename */ int kLdrDyldGetName(HKLDRMOD hMod, char *pszName, size_t cchName) { int rc; /* validate */ if (pszName && cchName) *pszName = '\0'; KLDRDYLD_VALIDATE_HKLDRMOD(hMod); KLDRHLP_VALIDATE_BUFFER(pszName, cchName); /* get sem & do work */ rc = kldrHlpSemRequest(); if (!rc) { rc = kldrDyldDoGetName(hMod, pszName, cchName); kldrHlpSemRelease(); } return rc; } /** * Gets the module filename. * * @returns 0 on success and pszFilename filled with the name. * @returns KLDR_ERR_INVALID_HANDLE or KLDR_ERR_BUFFER_OVERFLOW on failure. * @param hMod The module handle. * @param pszFilename Where to put the filename. * @param cchFilename The size of the filename buffer. * @see kLdrDyldGetName */ int kLdrDyldGetFilename(HKLDRMOD hMod, char *pszFilename, size_t cchFilename) { int rc; /* validate & initialize */ if (pszFilename && cchFilename); *pszFilename = '\0'; KLDRDYLD_VALIDATE_HKLDRMOD(hMod); KLDRHLP_VALIDATE_BUFFER(pszFilename, cchFilename); /* get sem & do work */ rc = kldrHlpSemRequest(); if (!rc) { rc = kldrDyldDoGetFilename(hMod, pszFilename, cchFilename); kldrHlpSemRelease(); } return rc; } /** * Queries the value and type of a symbol. * * @returns 0 on success and pValue and pfKind set. * @returns KLDR_ERR_INVALID_HANDLE or KLDR_ERR_SYMBOL_NOT_FOUND on failure. * @param hMod The module handle. * @param uSymbolOrdinal The symbol ordinal. This is ignored if pszSymbolName is non-zero. * @param pszSymbolName The symbol name. * @param pValue Where to put the symbol value. Optional if pfKind is non-zero. * @param pfKind Where to put the symbol kind flags. Optional if pValue is non-zero. */ int kLdrDyldQuerySymbol(HKLDRMOD hMod, uint32_t uSymbolOrdinal, const char *pszSymbolName, uintptr_t *pValue, uint32_t *pfKind) { int rc; /* validate & initialize */ if (pfKind) *pfKind = 0; if (pValue) *pValue = 0; if (!pfKind && !pValue) return KLDR_ERR_INVALID_PARAMETER; KLDRDYLD_VALIDATE_HKLDRMOD(hMod); KLDRHLP_VALIDATE_OPTIONAL_STRING(pszSymbolName); /* get sem & do work */ rc = kldrHlpSemRequest(); if (!rc) { rc = kldrDyldDoQuerySymbol(hMod, uSymbolOrdinal, pszSymbolName, pValue, pfKind); kldrHlpSemRelease(); } return rc; } /** * Gets prerequisite module. * * This will try load the requested module if necessary, returning it in the MAPPED state. * * @returns 0 on success. * @returns KLDR_ERR_MODULE_NOT_FOUND or I/O error on failure. * @param pszDll The name of the dll to look for. * @param pszDefPrefix Prefix than can be used when searching. * @param pszDefSuffix Suffix than can be used when searching. * @param enmSearch Method to use when locating the module. * @param fFlags Flags, a combintation of the KLDRYDLD_LOAD_FLAGS_* \#defines. * @param pDep The depentant module. * @param ppMod Where to put the module we get. */ int kldrDyldGetPrerequisite(const char *pszDll, const char *pszDefPrefix, const char *pszDefSuffix, KLDRDYLDSEARCH enmSearch, unsigned fFlags, PKLDRDYLDMOD pDep, PPKLDRDYLDMOD ppMod) { int rc; *ppMod = NULL; /* * Try find the module among the ones that's already loaded. */ rc = kldrDyldFindExistingModule(pszDll, pszDefPrefix, pszDefSuffix, enmSearch, fFlags, ppMod); if (!rc) { /* * See if the module should go on the stack and if it needs * some work before that. */ switch ((*ppMod)->enmState) { /* * The module is good, just add the dependency. */ case KLDRSTATE_GOOD: case KLDRSTATE_INITIALIZING: return kldrDyldModAddDep(*ppMod, pDep); /* * Prerequisites needs checking. */ case KLDRSTATE_PENDING_INITIALIZATION: (*ppMod)->fAlreadySeen = 0; case KLDRSTATE_PENDING_TERMINATION: break; /* * The module has been terminated so it need to be reloaded, have it's * prereqs loaded, fixed up and initialized before we can use it again. */ case KLDRSTATE_PENDING_GC: rc = kldrDyldModReload(*ppMod); if (rc) return rc; break; /* * It's just been loaded or reloaded in this session, we will push it * onto the stack even so to get a consisten init order with the other * states. */ case KLDRSTATE_MAPPED: case KLDRSTATE_RELOADED: break; /* * Forget it, we don't know how to deal with re-initialization here. */ case KLDRSTATE_TERMINATING: KLDRDYLD_ASSERT(!"KLDR_ERR_PREREQUISITE_MODULE_TERMINATING"); return KLDR_ERR_PREREQUISITE_MODULE_TERMINATING; /* * Invalid state. */ default: KLDRDYLD_ASSERT(!"invalid state"); break; } } else { /* * We'll have to load it from file. */ rc = kldrDyldFindNewModule(pszDll, pszDefPrefix, pszDefSuffix, enmSearch, fFlags, ppMod); if (!rc) rc = kldrDyldModMap(*ppMod); } /* * Push it onto the stack and add the dependency. */ if (!rc) rc = kldrDyldStackPushModule(*ppMod); if (!rc) rc = kldrDyldModAddDep(*ppMod, pDep); return rc; } /** * Worker for kLdrDyldLoad() and helper for kLdrDyldLoadExe(). * @internal */ static int kldrDyldDoLoad(const char *pszDll, const char *pszDefPrefix, const char *pszDefSuffix, KLDRDYLDSEARCH enmSearch, unsigned fFlags, PPKLDRDYLDMOD ppMod, char *pszErr, size_t cchErr) { int rc; /* * Try find the module among the ones that's already loaded. */ rc = kldrDyldFindExistingModule(pszDll, pszDefPrefix, pszDefSuffix, enmSearch, fFlags, ppMod); if (!rc) { /* * Because of initialization and termination routines this * get's kind of complicated. Even if the module is loaded, * we might end up having to resolve all dependencies to check * whether reloading or/and initialization is required. */ switch ((*ppMod)->enmState) { /* * Prerequisites are ok, so nothing to do really. */ case KLDRSTATE_GOOD: case KLDRSTATE_INITIALIZING: return kldrDyldModDynamicLoad(*ppMod); /* * Prerequisites needs checking. */ case KLDRSTATE_PENDING_INITIALIZATION: (*ppMod)->fAlreadySeen = 0; case KLDRSTATE_PENDING_TERMINATION: break; /* * The module has been terminated so it need to be reloaded, have it's * prereqs loaded, fixed up and initialized before we can use it again. */ case KLDRSTATE_PENDING_GC: rc = kldrDyldModReload(*ppMod); if (rc) return kldrDyldCopyError(rc, pszErr, cchErr); break; /* * Forget it, we don't know how to deal with re-initialization here. */ case KLDRSTATE_TERMINATING: KLDRDYLD_ASSERT(!"KLDR_ERR_MODULE_TERMINATING"); return KLDR_ERR_MODULE_TERMINATING; /* * Invalid state. */ default: KLDRDYLD_ASSERT(!"invalid state"); break; } } else { /* * We'll have to load it from file. */ rc = kldrDyldFindNewModule(pszDll, pszDefPrefix, pszDefSuffix, enmSearch, fFlags, ppMod); if (rc) return kldrDyldCopyError(rc, pszErr, cchErr); rc = kldrDyldModMap(*ppMod); } if (!rc) { /* * Create the stack frame of modules by resolving module prerequisites. */ uint32_t iStackBottom = kldrDyldStackNewFrame(); uint32_t iStack = iStackBottom; uint32_t iStackTop; rc = kldrDyldStackPushModule(*ppMod); while (!rc && iStack < g_cStackMods /* changes while we're in the loop */) { PKLDRDYLDMOD pMod = g_papStackMods[iStack]; switch (pMod->enmState) { /* * Load immediate prerequisite modules and push the ones needing * attention onto the stack. */ case KLDRSTATE_MAPPED: case KLDRSTATE_RELOADED: rc = kldrDyldModLoadPrerequisites(pMod, pszDll, pszDefPrefix, pszDefSuffix, enmSearch, fFlags); break; /* * Check dependencies. */ case KLDRSTATE_PENDING_TERMINATION: rc = kldrDyldModCheckPrerequisites(pMod); break; /* * Check its prerequisite modules the first time around. */ case KLDRSTATE_PENDING_INITIALIZATION: if (!pMod->fAlreadySeen) { pMod->fAlreadySeen = 1; rc = kldrDyldModCheckPrerequisites(pMod); } break; /* * These are ok. */ case KLDRSTATE_LOADED_PREREQUISITES: case KLDRSTATE_INITIALIZING: case KLDRSTATE_GOOD: break; /* * All other stats are invalid. */ default: KLDRDYLD_ASSERT(!"invalid state"); break; } /* next */ iStack++; } iStackTop = kldrDyldStackFrameCompleted(); /* * Apply fixups. */ for (iStack = iStackBottom; !rc && iStack < iStackTop; iStack++) { PKLDRDYLDMOD pMod = g_papStackMods[iStack]; if (pMod->enmState == KLDRSTATE_LOADED_PREREQUISITES) rc = kldrDyldModFixup(pMod); } #ifdef KLDRDYLD_STRICT if (!rc) for (iStack = iStackBottom; !rc && iStack < iStackTop; iStack++) KLDRDYLD_ASSERT(g_papStackMods[iStack]->enmState >= KLDRSTATE_FIXED_UP); #endif /* * Call the initializers (LIFO order). */ iStack = iStackBottom; while (!rc && iStack-- > iStackTop) { PKLDRDYLDMOD pMod = g_papStackMods[iStack]; if (pMod->enmState == KLDRSTATE_PENDING_INITIALIZATION) rc = kldrDyldModCallInit(pMod); } #ifdef KLDRDYLD_STRICT if (!rc) for (iStack = iStackBottom; !rc && iStack < iStackTop; iStack++) KLDRDYLD_ASSERT(g_papStackMods[iStack]->enmState == KLDRSTATE_GOOD); #endif /* * Complete the load by incrementing the dynamic load count of the * requested module (return handle is already set). */ if (!rc) { rc = kldrDyldModDynamicLoad(*ppMod); if (!rc) { kldrDyldStackDropFrame(iStackTop, iStackBottom, rc); kldrDyldModDeref(*ppMod); return rc; } } kldrDyldStackDropFrame(iStackTop, iStackBottom, rc); } else { /* If the reference count is 0 do a quick ref/deref to trigger destruction. */ kldrDyldModAddRef(*ppMod); kldrDyldModDeref(*ppMod); } /* * We've failed, copy/create error string. */ return kldrDyldCopyError(rc, pszErr, cchErr); } /** * Worker for kLdrDyldUnload(). * @internal */ static int kldrDyldDoUnload(PKLDRDYLDMOD pMod) { return kldrDyldModDynamicUnload(pMod); } /** * Worker for kLdrDyldFindByName(). * @internal */ static int kldrDyldDoFindByName(const char *pszDll, const char *pszDefPrefix, const char *pszDefSuffix, KLDRDYLDSEARCH enmSearch, unsigned fFlags, PPKLDRDYLDMOD ppMod) { return kldrDyldFindExistingModule(pszDll, pszDefPrefix, pszDefSuffix, enmSearch, fFlags, ppMod); } /** * Worker for kLdrDyldFindByAddress(). * @internal */ static int kldrDyldDoFindByAddress(uintptr_t Address, PPKLDRDYLDMOD ppMod, uint32_t *piSegment, uintptr_t *poffSegment) { /* Scan the segments of each module in the load list. */ PKLDRDYLDMOD pMod = kLdrDyldHead; while (pMod) { uint32_t iSeg; for (iSeg = 0; iSeg < pMod->pMod->cSegments; iSeg++) { uintmax_t off = (uintmax_t)Address - pMod->pMod->aSegments[iSeg].LoadAddress; if (off < pMod->pMod->aSegments[iSeg].cb) { *ppMod = pMod->hMod; if (piSegment) *piSegment = iSeg; if (poffSegment) *poffSegment = (uintptr_t)off; return 0; } } /* next */ pMod = pMod->Load.pNext; } return KLDR_ERR_MODULE_NOT_FOUND; } /** * Worker for kLdrDyldGetName(). * @internal */ static int kldrDyldDoGetName(PKLDRDYLDMOD pMod, char *pszName, size_t cchName) { return kldrDyldModGetName(pMod, pszName, cchName); } /** * Worker for kLdrDyldGetFilename(). * @internal */ static int kldrDyldDoGetFilename(PKLDRDYLDMOD pMod, char *pszFilename, size_t cchFilename) { return kldrDyldModGetFilename(pMod, pszFilename, cchFilename); } /** * Worker for kLdrDyldQuerySymbol(). * @internal */ static int kldrDyldDoQuerySymbol(PKLDRDYLDMOD pMod, uint32_t uSymbolOrdinal, const char *pszSymbolName, uintptr_t *pValue, uint32_t *pfKind) { return kldrDyldModQuerySymbol(pMod, uSymbolOrdinal, pszSymbolName, pValue, pfKind); } #if 0 void kldrLoadExe(PKLDREXEARGS pArgs) { /* * Copy the arguments into the globals and do load init. */ kLdrFlags = pArgs->fFlags; kLdrHlpMemCopy(kLdrLibraryPath, pArgs->szLibPath, KLDR_MIN(sizeof(pArgs->szLibPath), sizeof(kLdrLibraryPath))); int rc = kldrInit(); if (rc) kldrFailure(rc, "kLdr: Init failure, rc=%d\n", rc); /* * Open the executable module. */ PKLDRMOD pExe; kldrOpenExe(pArgs->szExecutable, &pExe); /* Map the segments. */ kldrModMapSegments(pExe); /* * This is the point where we switch to the executable * stack, allocating it if necessary. */ void *pvBottom; kldrModSetupStack(pExe, &pvBottom); kldrLoadExecSwitchStack(pvBottom); } void kldrLoadExeOnNewStack(void) { /* * Load all prerequisite modules. */ PKLDRMOD pCur; do for (pCur = kLdrModuleHead; pCur; pCur = pCur->pNext) { if (pCur->enmState >= KLDRSTATE_DEPS) continue; kldrModLoadDeps(pCur); } while (pCur); /* * Do fixups (FIFO). */ for (pCur = kLdrModuleHead; pCur; pCur = pCur->pNext) { if (pCur->enmState >= KLDRSTATE_FIXED) continue; kldrModFixup(pCur, 0); } /* * Do module initialization. */ for (pCur = kLdrModuleTail; pCur != kLdrModuleTail; pCur = pCur->pPrev) { if (pCur->enmState >= KLDRSTATE_INITED) continue; kldrModCallInit(pCur); } /* * Get the executable start address and commit the work that's been done. */ void *pvEntry; kldrModGetExeEntry(&pvEntry); for (pCur = kLdrModuleHead; pCur; pCur = pCur->pNext) if (pCur->enmState == KLDRSTATE_INITED) pCur->enmState = KLDRSTATE_LOADED; kldrHlpSemRelease(); /* * We're now ready for starting the executable code. */ kldrOSStartExe(pLdrModuleHead, pvEntry); } #endif /** * Do garbage collection. * * This isn't doing anything unless it's called from the last * load or unload call. */ static void kldrDyldDoModuleTerminationAndGarabageCollection(void) { PKLDRDYLDMOD pMod; /* * We don't do anything untill we're got rid of all re-entrant calls. * This will ensure that we get the most optimal termination order and * that we don't unload anything too early. */ if (g_cActiveLoadCalls || g_cActiveUnloadCalls || g_fActiveGC) return; g_fActiveGC = 1; /* * Release prerequisites to maximize the number of term calls pending. */ for (pMod = kLdrDyldHead; pMod; pMod = pMod->Load.pNext); { kldrDyldModAddRef(pMod); switch (pMod->enmState) { case KLDRSTATE_GOOD: case KLDRSTATE_PENDING_GC: break; case KLDRSTATE_PENDING_INITIALIZATION: case KLDRSTATE_PENDING_TERMINATION: kldrDyldModUnloadPrerequisites(pMod); break; default: KLDRDYLD_ASSERT(!"invalid GC state (a)"); break; } kldrDyldModDeref(pMod); } /* * Do termination calls (FIFO order) process new unloads. * The current algorithm here is a bit sluggish, but it ensure correct order. */ do { for (pMod = g_pkLdrDyldTermHead; pMod; pMod = pMod->Term.pNext) { int fRestart = 0; kldrDyldModAddRef(pMod); switch (pMod->enmState) { case KLDRSTATE_GOOD: case KLDRSTATE_PENDING_GC: break; case KLDRSTATE_PENDING_TERMINATION: kldrDyldModUnloadPrerequisites(pMod); kldrDyldModCallTerm(pMod); fRestart = 1; break; case KLDRSTATE_PENDING_INITIALIZATION: kldrDyldModUnloadPrerequisites(pMod); break; default: KLDRDYLD_ASSERT(!"invalid GC state (b)"); break; } kldrDyldModDeref(pMod); if (fRestart) break; } } while (pMod); /* * Unmap and destroy modules pending for GC. */ pMod = kLdrDyldHead; while (pMod) { PKLDRDYLDMOD pNext = pMod->Load.pNext; switch (pMod->enmState) { case KLDRSTATE_PENDING_GC: kldrDyldModAddRef(pMod); KLDRDYLD_ASSERT(pMod->cRefs == 1); pMod->enmState = KLDRSTATE_GC; kldrDyldModUnmap(pMod); KLDRDYLD_ASSERT(pMod->enmState == KLDRSTATE_PENDING_DESTROY); kldrDyldModDeref(pMod); break; /* the only other state that's valid at this point. */ case KLDRSTATE_GOOD: break; /* all other stats are invalid. */ default: KLDRDYLD_ASSERT(!"invalid GC state (c)"); break; } /* next */ pMod = pNext; } g_fActiveGC = 0; } /** * Starts loading a new module and its dependencies. * @returns Where the new stack frame starts. */ static uint32_t kldrDyldStackNewFrame(void) { #ifdef KLDRDYLD_STRICT /* check that all modules are in the correct state */ PKLDRDYLDMOD pMod = kLdrDyldHead; while (pMod) { //KLDRDYLD_ASSERT(pMod->enmState == KLDRSTATE_LOADED); /* next */ pMod = pMod->Load.pNext; } #endif return g_cStackMods; } /** * Records the module. * * @return 0 on success, KLDR_ERR_NO_MEMORY if we can't expand the table. * @param pMod The module to record. */ int kldrDyldStackPushModule(PKLDRDYLDMOD pMod) { int rc; /* * Grow the stack if necessary. */ if (g_cStackMods + 1 > g_cStackModsAllocated) { uint32_t cNew = g_cStackModsAllocated ? g_cStackModsAllocated * 2 : 128; void *pvOld = g_papStackMods; void *pvNew = kldrHlpAlloc(cNew * sizeof(g_papStackMods[0])); if (!pvNew) return KLDR_ERR_NO_MEMORY; kLdrHlpMemCopy(pvNew, pvOld, g_cStackMods * sizeof(g_papStackMods[0])); g_papStackMods = (PPKLDRDYLDMOD)pvNew; kldrHlpFree(pvOld); } /* * Add a reference and push the module onto the stack. */ rc = kldrDyldModAddRef(pMod); if (!rc) g_papStackMods[g_cStackMods++] = pMod; return rc; } /** * The frame has been completed. * * @returns Where the frame ends. */ static int kldrDyldStackFrameCompleted(void) { return g_cStackMods; } /** * Done with the stack frame, dereference all the module in it. * * @param iStackTop The top of the stack frame. * @param iStackBottom The bottom of the stack frame. * @param rc Used for state verification. */ static void kldrDyldStackDropFrame(uint32_t iStackTop, uint32_t iStackBottom, int rc) { uint32_t iStack; KLDRDYLD_ASSERT(iStackBottom <= g_cStackMods); KLDRDYLD_ASSERT(iStackTop == g_cStackMods); /* * First pass, cleanup modules in an obvious 'failed' state so we don't * leave any of the non-reentrant states behind. */ for (iStack = iStackBottom; iStack < iStackTop; iStack++) { PKLDRDYLDMOD pMod = g_papStackMods[--iStackTop]; switch (pMod->enmState) { /* * Unmap freshly mapped so it can be destroyed. */ case KLDRSTATE_MAPPED: KLDRDYLD_ASSERT(rc); kldrDyldModUnmap(pMod); KLDRDYLD_ASSERT(pMod->enmState == KLDRSTATE_PENDING_DESTROY); break; /* * Reload is reverted to it's old state. */ case KLDRSTATE_RELOADED: KLDRDYLD_ASSERT(rc); pMod->enmState = KLDRSTATE_PENDING_GC; break; /* * Unload prerequisites and unmap modules loaded in this frame. */ case KLDRSTATE_LOADED_PREREQUISITES: case KLDRSTATE_FIXED_UP: /** @todo fix reload state mess. */ KLDRDYLD_ASSERT(rc); kldrDyldModUnloadPrerequisites(pMod); KLDRDYLD_ASSERT(pMod->enmState == KLDRSTATE_PENDING_GC); kldrDyldModUnmap(pMod); KLDRDYLD_ASSERT(pMod->enmState == KLDRSTATE_PENDING_DESTROY); break; /* * Ok states. */ case KLDRSTATE_INITIALIZING: KLDRDYLD_ASSERT(g_cActiveLoadCalls > 0); break; case KLDRSTATE_TERMINATING: KLDRDYLD_ASSERT(rc); KLDRDYLD_ASSERT(g_fActiveGC); break; case KLDRSTATE_PENDING_TERMINATION: case KLDRSTATE_PENDING_INITIALIZATION: case KLDRSTATE_PENDING_GC: case KLDRSTATE_PENDING_DESTROY: KLDRDYLD_ASSERT(rc); break; case KLDRSTATE_GOOD: break; /* * Bad states. */ default: KLDRDYLD_ASSERT(!"drop frame bad state (a)"); break; } } /* * Second pass, release the references to the modules on the stack. */ while (iStackTop > iStackBottom) { /* * Pop a module. */ PKLDRDYLDMOD pMod = g_papStackMods[--iStackTop]; g_cStackMods = iStackTop; /* * Validate the state. */ switch (pMod->enmState) { /* * Ok states */ case KLDRSTATE_INITIALIZING: KLDRDYLD_ASSERT(g_cActiveLoadCalls > 0); break; case KLDRSTATE_TERMINATING: KLDRDYLD_ASSERT(rc); KLDRDYLD_ASSERT(g_fActiveGC); break; case KLDRSTATE_PENDING_INITIALIZATION: case KLDRSTATE_PENDING_TERMINATION: case KLDRSTATE_PENDING_GC: case KLDRSTATE_PENDING_DESTROY: KLDRDYLD_ASSERT(rc); break; case KLDRSTATE_GOOD: break; /* * Bad states. */ default: KLDRDYLD_ASSERT(!"drop frame bad state (b)"); break; } /* * Release it. */ kldrDyldModDeref(pMod); } } /** * Panic / failure * * @returns rc if we're in a position where we can return. * @param rc Return code. * @param pszFormat Message string. Limited fprintf like formatted. * @param ... Message string arguments. */ int kldrFailure(int rc, const char *pszFormat, ...) { kldrHlpExit(1); return rc; } /** * Copies the error string to the user buffer. * * @returns rc. * @param rc The status code. * @param pszErr Where to copy the error string to. * @param cchErr The size of the destination buffer. */ static int kldrDyldCopyError(int rc, char *pszErr, size_t cchErr) { size_t cchToCopy; /* if no error string, format the rc into a string. */ if (!g_szkLdrDyldError[0] && rc) kldrHlpInt2Ascii(g_szkLdrDyldError, sizeof(g_szkLdrDyldError), rc, 10); /* copy it if we got something. */ if (cchErr && pszErr && g_szkLdrDyldError[0]) { cchToCopy = kLdrHlpStrLen(g_szkLdrDyldError); if (cchToCopy >= cchErr) cchToCopy = cchErr - 1; kLdrHlpMemCopy(pszErr, g_szkLdrDyldError, cchToCopy); pszErr[cchToCopy] = '\0'; } return rc; }