/* $Id: $ */ /** @file * * kLdr - The Dynamic Loader, Dyld module methods. * * 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 "kLdrInternal.h" #include "kLdrHlp.h" /******************************************************************************* * Defined Constants And Macros * *******************************************************************************/ /** @def KLDRDYLDMOD_STRICT * Define KLDRDYLDMOD_STRICT to enabled strict checks in kLdrDyld. */ #define KLDRDYLDMOD_STRICT 1 /** @def KLDRDYLDMOD_ASSERT * Assert that an expression is true when KLDRDYLD_STRICT is defined. */ #ifdef KLDRDYLDMOD_STRICT # define KLDRDYLDMOD_ASSERT(expr) kldrHlpAssert(expr) #else # define KLDRDYLDMOD_ASSERT(expr) do {} while (0) #endif /** * Creates a module from the specified file provider instance. * * @returns 0 on success and *ppMod pointing to the new instance. * On failure a non-zero kLdr status code is returned. * @param pRdr The file provider instance. * @param ppMod Where to put the pointer to the new module on success. */ int kldrDyldModCreate(PKLDRRDR pRdr, PPKLDRDYLDMOD ppMod) { PKLDRDYLDMOD pMod; PKLDRMOD pRawMod; int rc; *ppMod = NULL; /* * Try open an module interpreter. */ rc = kLdrModOpenFromRdr(pRdr, &pRawMod); if (rc) return kldrDyldFailure(rc, "%s: %rc", kLdrRdrName(pRdr), rc); /* * Allocate a new dyld module. */ pMod = (PKLDRDYLDMOD)kldrHlpAlloc(sizeof(*pMod)); if (pMod) { pMod->enmState = KLDRSTATE_OPEN; pMod->pMod = pRawMod; pMod->hMod = pMod; pMod->cDepRefs = pMod->cDynRefs = pMod->cRefs = 0; pMod->fExecutable = 0; switch (pRawMod->enmType) { case KLDRTYPE_EXECUTABLE_FIXED: case KLDRTYPE_EXECUTABLE_RELOCATABLE: case KLDRTYPE_EXECUTABLE_PIC: pMod->fExecutable = 1; break; } pMod->fGlobalOrSpecific = 0; pMod->fBindable = 0; pMod->fInitList = 0; pMod->fAlreadySeen = 0; pMod->fMapped = 0; pMod->f26Reserved = 0; pMod->InitTerm.pNext = NULL; pMod->InitTerm.pPrev = NULL; pMod->Bind.pNext = NULL; pMod->Bind.pPrev = NULL; pMod->cPrereqs = 0; pMod->papPrereqs = NULL; pMod->u32MagicHead = KLDRDYMOD_MAGIC; pMod->u32MagicTail = KLDRDYMOD_MAGIC; /* it. */ pMod->Load.pNext = NULL; pMod->Load.pPrev = kLdrDyldTail; if (kLdrDyldTail) kLdrDyldTail->Load.pNext = pMod; else kLdrDyldHead = pMod; kLdrDyldTail = pMod; /* we're good. */ *ppMod = pMod; rc = 0; } else { kLdrModClose(pRawMod); rc = KLDR_ERR_NO_MEMORY; } return rc; } /** * Creates a module for a native module. * * @returns 0 on success and *ppMod pointing to the new instance. * On failure a non-zero kLdr status code is returned. * @param hNativeModule The native handle. * @param ppMod Where to put the pointer to the new module on success. * @remark This function ain't finalized yet. */ int kldrDyldModCreateNative(uintptr_t hNativeModule) { #if 0 /* * Check if this module is already loaded by the native OS loader. */ rc = kld { #ifdef __OS2__ HMODULE hmod = NULLHANDLE; APIRET rc = DosQueryModuleHandle(kLdrRdrName(pRdr), &hmod); if (!rc) #elif defined(__WIN__) HMODULE hmod = NULL; if (GetModuleHandle(kLdrRdrName(pRdr)) #else # error "Port me" #endif } #endif return -1; } /** * Destroys a module pending destruction. * * @param pMod The module in question. */ void kldrDyldModDestroy(PKLDRDYLDMOD pMod) { /* * Validate the state. */ switch (pMod->enmState) { case KLDRSTATE_PENDING_DESTROY: case KLDRSTATE_GC: break; default: KLDRDYLDMOD_ASSERT(!"Invalid state"); break; } KLDRDYLDMOD_ASSERT(!pMod->fInitList); KLDRDYLDMOD_ASSERT(!pMod->cDynRefs); KLDRDYLDMOD_ASSERT(!pMod->cDepRefs); /* * Ensure that the module is unmapped. */ if (pMod->fMapped) kldrDyldModUnmap(pMod); } /** * Unlinks the module from any list it might be in. * It is assumed that the module is at least linked into the load list. * * @param pMod The moduel. */ static void kldrDyldModUnlink(PKLDRDYLDMOD pMod) { /* load list */ if (pMod->Load.pNext) pMod->Load.pNext->Load.pPrev = pMod->Load.pPrev; else kLdrDyldTail = pMod->Load.pPrev; if (pMod->Load.pPrev) pMod->Load.pPrev->Load.pNext = pMod->Load.pNext; else kLdrDyldHead = pMod->Load.pNext; /* bind list */ if (pMod->fBindable) kldrDyldModClearBindable(pMod); /* init term */ if (pMod->fInitList) { KLDRDYLDMOD_ASSERT(pMod->enmState < KLDRSTATE_INITIALIZATION_FAILED); pMod->fInitList = 0; if (pMod->InitTerm.pNext) pMod->InitTerm.pNext->InitTerm.pPrev = pMod->InitTerm.pPrev; else g_pkLdrDyldInitTail = pMod->InitTerm.pPrev; if (pMod->InitTerm.pPrev) pMod->InitTerm.pPrev->InitTerm.pNext = pMod->InitTerm.pNext; else g_pkLdrDyldInitHead = pMod->InitTerm.pNext; } else if (pMod->enmState > KLDRSTATE_INITIALIZATION_FAILED) { KLDRDYLDMOD_ASSERT(pMod->enmState >= KLDRSTATE_GOOD); if (pMod->InitTerm.pNext) pMod->InitTerm.pNext->InitTerm.pPrev = pMod->InitTerm.pPrev; else g_pkLdrDyldTermTail = pMod->InitTerm.pPrev; if (pMod->InitTerm.pPrev) pMod->InitTerm.pPrev->InitTerm.pNext = pMod->InitTerm.pNext; else g_pkLdrDyldTermHead = pMod->InitTerm.pNext; } pMod->InitTerm.pNext = NULL; pMod->InitTerm.pPrev = NULL; } /** * Adds a reference to the module making sure it won't be freed just yet. * * @param pMod The module. */ void kldrDyldModAddRef(PKLDRDYLDMOD pMod) { pMod->cRefs++; } /** * Dereference a module. * * @param pMod */ void kldrDyldModDeref(PKLDRDYLDMOD pMod) { /* validate input */ KLDRDYLDMOD_ASSERT(pMod->cRefs > 0); KLDRDYLDMOD_ASSERT(pMod->cRefs >= pMod->cDepRefs + pMod->cDynRefs); KLDRDYLDMOD_ASSERT(pMod->enmState > KLDRSTATE_INVALID && pMod->enmState <= KLDRSTATE_END); /* decrement. */ if (pMod->cRefs > 0) pMod->cRefs--; /* execute delayed freeing. */ if ( pMod->enmState == KLDRSTATE_DESTROYED && !pMod->cRefs) kldrHlpFree(pMod); } /** * Increment the count of modules depending on this module. * * @param pMod The module. * @param pDep The module which depends on us. */ void kldrDyldModAddDep(PKLDRDYLDMOD pMod, PKLDRDYLDMOD pDep) { (void)pDep; /* validate state */ switch (pMod->enmState) { case KLDRSTATE_MAPPED: case KLDRSTATE_RELOADED: case KLDRSTATE_LOADED_PREREQUISITES: case KLDRSTATE_RELOADED_LOADED_PREREQUISITES: case KLDRSTATE_PENDING_INITIALIZATION: case KLDRSTATE_INITIALIZING: case KLDRSTATE_GOOD: break; default: KLDRDYLDMOD_ASSERT(!"invalid state"); break; } KLDRDYLDMOD_ASSERT(pMod->enmState > KLDRSTATE_INVALID && pMod->enmState <= KLDRSTATE_END); pMod->cRefs++; pMod->cDepRefs++; } /** * Drop a dependency. * * @param pMod The module. * @param pDep The module which depends on us. */ void kldrDyldModRemoveDep(PKLDRDYLDMOD pMod, PKLDRDYLDMOD pDep) { KLDRDYLDMOD_ASSERT(pMod->cDepRefs > 0); if (pMod->cDepRefs == 0) return; KLDRDYLDMOD_ASSERT(pMod->cDepRefs <= pMod->cRefs); KLDRDYLDMOD_ASSERT(pMod->enmState >= KLDRSTATE_MAPPED && pMod->enmState <= KLDRSTATE_PENDING_DESTROY); pMod->cRefs--; pMod->cDepRefs--; if ( pMod->cDepRefs > 0 || pMod->cDynRefs > 0) return; /* * The module should be unloaded. */ kldrDyldModUnloadPrerequisites(pMod); } /** * Increment the dynamic load count. * * @returns 0 * @param pMod The module. */ int kldrDyldModDynamicLoad(PKLDRDYLDMOD pMod) { KLDRDYLDMOD_ASSERT( pMod->enmState == KLDRSTATE_GOOD || pMod->enmState == KLDRSTATE_PENDING_INITIALIZATION || pMod->enmState == KLDRSTATE_INITIALIZING); pMod->cRefs++; pMod->cDynRefs++; return 0; } /** * Decrement the dynamic load count of the module and unload the module * if the total reference count reaches zero. * * This may cause a cascade of unloading to occure. See kldrDyldModUnloadPrerequisites(). * * @returns status code. * @retval 0 on success. * @retval KLDR_ERR_NOT_LOADED_DYNAMICALLY if the module wasn't loaded dynamically. * @param pMod The module to unload. */ int kldrDyldModDynamicUnload(PKLDRDYLDMOD pMod) { if (pMod->cDynRefs == 0) return KLDR_ERR_NOT_LOADED_DYNAMICALLY; KLDRDYLDMOD_ASSERT(pMod->cDynRefs <= pMod->cRefs); KLDRDYLDMOD_ASSERT(pMod->enmState == KLDRSTATE_GOOD); pMod->cRefs--; pMod->cDynRefs--; if ( pMod->cDynRefs > 0 || pMod->cDepRefs > 0) return 0; /* * The module should be unloaded. */ kldrDyldModUnloadPrerequisites(pMod); return 0; } /** * Worker for kldrDyldModUnloadPrerequisites. * * @returns The number of modules that now can be unloaded. * @param pMod The module in question. */ static uint32_t kldrDyldModUnloadPrerequisitesOne(PKLDRDYLDMOD pMod) { PKLDRDYLDMOD pMod2; uint32_t cToUnload = 0; uint32_t i; KLDRDYLDMOD_ASSERT(pMod->papPrereqs); /* * Release the one in this module. */ for (i = 0; i < pMod->cPrereqs; i++) { pMod2 = pMod->papPrereqs[i]; if (pMod2) { /* do the derefering ourselves or we'll end up in a recursive loop here. */ KLDRDYLDMOD_ASSERT(pMod2->cDepRefs > 0); KLDRDYLDMOD_ASSERT(pMod2->cRefs >= pMod2->cDepRefs); pMod2->cDepRefs--; pMod2->cRefs--; cToUnload += !pMod2->cRefs; } } /* * Change the state */ switch (pMod->enmState) { case KLDRSTATE_LOADED_PREREQUISITES: case KLDRSTATE_FIXED_UP: pMod->enmState = KLDRSTATE_PENDING_DESTROY; kldrDyldModUnlink(pMod); break; case KLDRSTATE_PENDING_INITIALIZATION: pMod->enmState = KLDRSTATE_PENDING_GC; break; case KLDRSTATE_RELOADED_FIXED_UP: case KLDRSTATE_RELOADED_LOADED_PREREQUISITES: case KLDRSTATE_GOOD: pMod->enmState = KLDRSTATE_PENDING_TERMINATION; break; case KLDRSTATE_INITIALIZATION_FAILED: break; default: KLDRDYLDMOD_ASSERT(!"invalid state"); break; } return cToUnload; } /** * This is the heart of the unload code. * * It will recursivly (using the load list) initiate module unloading * of all affected modules. * * This function will cause a state transition to PENDING_DESTROY, PENDING_GC * or PENDING_TERMINATION depending on the module state. There is one exception * to this, and that's INITIALIZATION_FAILED, where the state will not be changed. * * @param pMod The module which prerequisites should be unloaded. */ void kldrDyldModUnloadPrerequisites(PKLDRDYLDMOD pMod) { uint32_t cToUnload; /* sanity */ #ifdef KLDRDYLD_STRICT { PKLDRDYLDMOD pMod2; for (pMod2 = kLdrDyldHead; pMod2; pMod2 = pMod2->Load.pNext) KLDRDYLDMOD_ASSERT(pMod2->enmState != KLDRSTATE_GOOD || pMod2->cRefs); } #endif KLDRDYLDMOD_ASSERT(pMod->papPrereqs); /* * Unload prereqs of the module we're called on first. */ cToUnload = kldrDyldModUnloadPrerequisitesOne(pMod); /* * Iterate the load list in a cyclic manner until there are no more * modules that can be pushed on into unloading. */ while (cToUnload) { cToUnload = 0; for (pMod = kLdrDyldHead; pMod; pMod = pMod->Load.pNext) { if ( pMod->cRefs || pMod->enmState >= KLDRSTATE_PENDING_TERMINATION || pMod->enmState < KLDRSTATE_LOADED_PREREQUISITES) continue; cToUnload += kldrDyldModUnloadPrerequisitesOne(pMod); } } } int kldrDyldModLoadPrerequisites(PKLDRDYLDMOD pMod, const char *pszPrefix, const char *pszSuffix, KLDRDYLDSEARCH enmSearch, unsigned fFlags) { return -1; } int kldrDyldModCheckPrerequisites(PKLDRDYLDMOD pMod) { return -1; } /** * Marks the module as global instead of being specific. * * A global module can be a matching result when the request * doesn't specify a path. A specific module will not match * unless the path also matches. * * @param pMod The module. */ void kldrDyldModMarkGlobal(PKLDRDYLDMOD pMod) { pMod->fGlobalOrSpecific = 1; } /** * Marks the module as specific instead of global. * * See kldrDyldModMarkGlobal for an explanation of the two terms. * * @param pMod The module. */ void kldrDyldModMarkSpecific(PKLDRDYLDMOD pMod) { pMod->fGlobalOrSpecific = 0; } /** * Marks a module as bindable, i.e. it'll be considered when * resolving names the unix way. * * @param pMod The module. * @param fDeep When set the module will be inserted at the head of the * module list used to resolve symbols. This means that the * symbols in this module will be prefered of all the other * modules. */ void kldrDyldModSetBindable(PKLDRDYLDMOD pMod, unsigned fDeep) { KLDRDYLDMOD_ASSERT(pMod->enmState >= KLDRSTATE_OPEN && pMod->enmState < KLDRSTATE_PENDING_GC); if (!pMod->fBindable) { pMod->fBindable = 1; if (!fDeep) { pMod->Bind.pNext = NULL; pMod->Bind.pPrev = g_pkLdrDyldBindTail; if (g_pkLdrDyldBindTail) g_pkLdrDyldBindTail->Bind.pNext = pMod; else g_pkLdrDyldBindHead = pMod; g_pkLdrDyldBindTail = pMod; } else { pMod->Bind.pPrev = NULL; pMod->Bind.pNext = g_pkLdrDyldBindHead; if (g_pkLdrDyldBindHead) g_pkLdrDyldBindHead->Bind.pPrev = pMod; else g_pkLdrDyldBindTail = pMod; g_pkLdrDyldBindHead = pMod; } } } /** * Marks a module as not bindable, i.e. it will not be considered when * resolving names the unix way. * * @param pMod The module. */ void kldrDyldModClearBindable(PKLDRDYLDMOD pMod) { KLDRDYLDMOD_ASSERT(pMod->enmState >= KLDRSTATE_OPEN && pMod->enmState < KLDRSTATE_PENDING_DESTROY); if (pMod->fBindable) { pMod->fBindable = 0; if (pMod->Bind.pPrev) pMod->Bind.pPrev->Bind.pNext = pMod->Bind.pNext; else g_pkLdrDyldBindHead = pMod->Bind.pNext; if (pMod->Bind.pNext) pMod->Bind.pNext->Bind.pPrev = pMod->Bind.pPrev; else g_pkLdrDyldBindTail = pMod->Bind.pPrev; pMod->Bind.pNext = NULL; pMod->Bind.pPrev = NULL; } } /** * Maps an open module. * * On success the module will be in the MAPPED state. * * @returns 0 on success, non-zero native OS or kLdr status code on failure. * @param pMod The module which needs to be unmapped and set pending for destruction. */ int kldrDyldModMap(PKLDRDYLDMOD pMod) { int rc; /* sanity */ KLDRDYLDMOD_ASSERT(pMod->cRefs > 0); KLDRDYLDMOD_ASSERT(pMod->enmState == KLDRSTATE_OPEN); KLDRDYLDMOD_ASSERT(!pMod->fMapped); if (pMod->fMapped) return 0; /* do the job. */ rc = kLdrModMap(pMod->pMod); if (!rc) { pMod->fMapped = 1; pMod->enmState = KLDRSTATE_MAPPED; } return rc; } /** * Unmaps the module, unlinks it from everywhere marks it PENDING_DESTROY. * * @returns 0 on success, non-zero native OS or kLdr status code on failure. * @param pMod The module which needs to be unmapped and set pending for destruction. */ int kldrDyldModUnmap(PKLDRDYLDMOD pMod) { int rc; /* sanity */ KLDRDYLDMOD_ASSERT(pMod->cRefs > 0); KLDRDYLDMOD_ASSERT(pMod->fMapped); switch (pMod->enmState) { case KLDRSTATE_MAPPED: case KLDRSTATE_GC: case KLDRSTATE_PENDING_DESTROY: break; default: KLDRDYLDMOD_ASSERT(!"invalid state"); return -1; } /* do the job. */ rc = kLdrModUnmap(pMod->pMod); if (!rc) { pMod->fMapped = 0; if (pMod->enmState < KLDRSTATE_PENDING_DESTROY) { pMod->enmState = KLDRSTATE_PENDING_DESTROY; kldrDyldModUnlink(pMod); } } return rc; } /** * Applies fixups to a module which prerequisistes has been * successfully loaded. * * @returns 0 on success, non-zero native OS or kLdr status code on failure. * @param pMod The module which needs to be unmapped and set pending for destruction. */ int kldrDyldModFixup(PKLDRDYLDMOD pMod) { int rc; /* sanity */ KLDRDYLDMOD_ASSERT(pMod->cRefs > 0); KLDRDYLDMOD_ASSERT( pMod->enmState == KLDRSTATE_LOADED_PREREQUISITES || pMod->enmState == KLDRSTATE_RELOADED_LOADED_PREREQUISITES); /* do the job */ rc = kLdrModFixupMapping(pMod->pMod, NULL, NULL);/// @todo fixme. if (!rc) pMod->enmState = KLDRSTATE_FIXED_UP; return rc; } int kldrDyldModCallInit(PKLDRDYLDMOD pMod); void kldrDyldModCallTerm(PKLDRDYLDMOD pMod); int kldrDyldModReload(PKLDRDYLDMOD pMod); int kldrDyldModAttachThread(PKLDRDYLDMOD pMod); int kldrDyldModDetachThread(PKLDRDYLDMOD pMod); int kldrDyldModGetStackInfo(PKLDRDYLDMOD pMod, void **ppvStack, size_t *pcbStack); int kldrDyldModStartExe(PKLDRDYLDMOD pMod); int kldrDyldModGetName(PKLDRDYLDMOD pMod, char *pszName, size_t cchName); int kldrDyldModGetFilename(PKLDRDYLDMOD pMod, char *pszFilename, size_t cchFilename); int kldrDyldModQuerySymbol(PKLDRDYLDMOD pMod, uint32_t uSymbolOrdinal, const char *pszSymbolName, uintptr_t *pValue, uint32_t *pfKind);