summaryrefslogtreecommitdiffstats
path: root/src/lib/kStuff/kLdr/kLdrDyldMod.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/kStuff/kLdr/kLdrDyldMod.c')
-rw-r--r--src/lib/kStuff/kLdr/kLdrDyldMod.c1300
1 files changed, 1300 insertions, 0 deletions
diff --git a/src/lib/kStuff/kLdr/kLdrDyldMod.c b/src/lib/kStuff/kLdr/kLdrDyldMod.c
new file mode 100644
index 0000000..b25f6fc
--- /dev/null
+++ b/src/lib/kStuff/kLdr/kLdrDyldMod.c
@@ -0,0 +1,1300 @@
+/* $Id: kLdrDyldMod.c 81 2016-08-18 22:10:38Z bird $ */
+/** @file
+ * kLdr - The Dynamic Loader, Dyld module methods.
+ */
+
+/*
+ * Copyright (c) 2006-2007 Knut St. Osmundsen <bird-kStuff-spamix@anduin.net>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*******************************************************************************
+* Header Files *
+*******************************************************************************/
+#include <k/kLdr.h>
+#include "kLdrInternal.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) kHlpAssert(expr)
+#else
+# define KLDRDYLDMOD_ASSERT(expr) do {} while (0)
+#endif
+
+/*******************************************************************************
+* Internal Functions *
+*******************************************************************************/
+static void kldrDyldModUnlink(PKLDRDYLDMOD pMod);
+
+
+
+/**
+ * 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 fFlags Load/search flags.
+ * @param ppMod Where to put the pointer to the new module on success.
+ */
+int kldrDyldModCreate(PKRDR pRdr, KU32 fFlags, PPKLDRDYLDMOD ppMod)
+{
+ PKLDRDYLDMOD pMod;
+ PKLDRMOD pRawMod;
+ int rc;
+
+ *ppMod = NULL;
+
+/** @todo deal with fFlags (exec/dll) */
+/** @todo Check up the cpu architecture. */
+
+ /*
+ * Try open an module interpreter.
+ */
+ rc = kLdrModOpenFromRdr(pRdr, 0 /*fFlags*/, KCPUARCH_UNKNOWN, &pRawMod);
+ if (rc)
+ return kldrDyldFailure(rc, "%s: %rc", kRdrName(pRdr), rc);
+
+ /*
+ * Match the module aginst the load flags.
+ */
+ switch (pRawMod->enmType)
+ {
+ case KLDRTYPE_EXECUTABLE_FIXED:
+ case KLDRTYPE_EXECUTABLE_RELOCATABLE:
+ case KLDRTYPE_EXECUTABLE_PIC:
+ if (!(fFlags & KLDRDYLD_LOAD_FLAGS_EXECUTABLE))
+ {
+ kLdrModClose(pRawMod);
+ return KLDR_ERR_NOT_EXE;
+ }
+ break;
+
+ case KLDRTYPE_OBJECT: /* We can handle these as DLLs. */
+ case KLDRTYPE_SHARED_LIBRARY_FIXED:
+ case KLDRTYPE_SHARED_LIBRARY_RELOCATABLE:
+ case KLDRTYPE_SHARED_LIBRARY_PIC:
+ case KLDRTYPE_FORWARDER_DLL:
+ if (fFlags & KLDRDYLD_LOAD_FLAGS_EXECUTABLE)
+ {
+ kLdrModClose(pRawMod);
+ return KLDR_ERR_NOT_DLL;
+ }
+ break;
+
+ default:
+ KLDRDYLDMOD_ASSERT(!"Bad enmType!");
+ case KLDRTYPE_CORE:
+ return fFlags & KLDRDYLD_LOAD_FLAGS_EXECUTABLE ? KLDR_ERR_NOT_EXE : KLDR_ERR_NOT_DLL;
+ }
+
+ /*
+ * Allocate a new dyld module.
+ */
+ pMod = (PKLDRDYLDMOD)kHlpAlloc(sizeof(*pMod));
+ if (pMod)
+ {
+ pMod->enmState = KLDRSTATE_OPEN;
+ pMod->pMod = pRawMod;
+ pMod->hMod = pMod;
+ pMod->cDepRefs = pMod->cDynRefs = pMod->cRefs = 0;
+ switch (pRawMod->enmType)
+ {
+ case KLDRTYPE_EXECUTABLE_FIXED:
+ case KLDRTYPE_EXECUTABLE_RELOCATABLE:
+ case KLDRTYPE_EXECUTABLE_PIC:
+ pMod->fExecutable = 1;
+ break;
+ default:
+ pMod->fExecutable = 0;
+ break;
+ }
+ pMod->fGlobalOrSpecific = 0;
+ pMod->fBindable = 0;
+ pMod->fInitList = 0;
+ pMod->fAlreadySeen = 0;
+ pMod->fMapped = 0;
+ pMod->fAllocatedTLS = 0;
+ pMod->f25Reserved = 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;
+
+ /* deal with the remaining flags. */
+ if (fFlags & KLDRYDLD_LOAD_FLAGS_SPECIFIC_MODULE)
+ kldrDyldModMarkSpecific(pMod);
+ else
+ kldrDyldModMarkGlobal(pMod);
+
+ if (fFlags & KLDRYDLD_LOAD_FLAGS_GLOBAL_SYMBOLS)
+ kldrDyldModSetBindable(pMod, 0 /* not deep binable */);
+ else
+ kldrDyldModClearBindable(pMod);
+
+ /*
+ * We're good.
+ */
+ *ppMod = pMod;
+ rc = 0;
+ }
+ else
+ {
+ kLdrModClose(pRawMod);
+ rc = KERR_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(KUPTR hNativeModule)
+{
+#if 0
+ /*
+ * Check if this module is already loaded by the native OS loader.
+ */
+ rc = kld
+ {
+#if K_OS == K_OS_OS2
+ HMODULE hmod = NULLHANDLE;
+ APIRET rc = DosQueryModuleHandle(kRdrName(pRdr), &hmod);
+ if (!rc)
+
+#elif K_OS == K_OS_WINDOWS
+ HMODULE hmod = NULL;
+ if (GetModuleHandle(kRdrName(pRdr))
+
+#else
+# error "Port me"
+#endif
+ }
+#endif
+ return -1;
+}
+
+
+/**
+ * Destroys a module pending destruction.
+ *
+ * @param pMod The module in question.
+ */
+void kldrDyldModDestroy(PKLDRDYLDMOD pMod)
+{
+ int rc;
+
+ /*
+ * 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->fAllocatedTLS)
+ {
+ kLdrModFreeTLS(pMod->pMod, KLDRMOD_INT_MAP);
+ pMod->fAllocatedTLS = 0;
+ }
+ if (pMod->fMapped)
+ {
+ rc = kLdrModUnmap(pMod->pMod); KLDRDYLDMOD_ASSERT(!rc);
+ pMod->fMapped = 0;
+ }
+
+ /*
+ * Ensure it's unlinked from all chains.
+ */
+ if (pMod->enmState < KLDRSTATE_PENDING_DESTROY)
+ kldrDyldModUnlink(pMod);
+
+ /*
+ * Free everything associated with the module.
+ */
+ /* the prerequisite array. */
+ if (pMod->papPrereqs)
+ {
+ KU32 i = pMod->cPrereqs;
+ while (i-- > 0)
+ {
+ KLDRDYLDMOD_ASSERT(pMod->papPrereqs[i] == NULL);
+ pMod->papPrereqs[i] = NULL;
+ }
+
+ kHlpFree(pMod->papPrereqs);
+ pMod->papPrereqs = NULL;
+ pMod->cPrereqs = 0;
+ }
+
+ /* the module interpreter. */
+ if (pMod->pMod)
+ {
+ rc = kLdrModClose(pMod->pMod); KLDRDYLDMOD_ASSERT(!rc);
+ pMod->pMod = NULL;
+ }
+
+
+ /*
+ * Finally, change the module state and free the module if
+ * there are not more references to it. If somebody is still
+ * referencing it, postpone the freeing to Deref.
+ */
+ pMod->enmState = KLDRSTATE_DESTROYED;
+ if (!pMod->cRefs)
+ {
+ pMod->u32MagicHead = 1;
+ pMod->u32MagicTail = 2;
+ kHlpFree(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;
+}
+
+
+/**
+ * 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;
+ }
+}
+
+
+/**
+ * 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;
+}
+
+
+/**
+ * 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)
+ {
+ pMod->u32MagicHead = 1;
+ pMod->u32MagicTail = 2;
+ kHlpFree(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 KU32 kldrDyldModUnloadPrerequisitesOne(PKLDRDYLDMOD pMod)
+{
+ PKLDRDYLDMOD pMod2;
+ KU32 cToUnload = 0;
+ KU32 i;
+
+ KLDRDYLDMOD_ASSERT(pMod->papPrereqs || !pMod->cPrereqs);
+
+ /*
+ * Release the one in this module.
+ */
+ for (i = 0; i < pMod->cPrereqs; i++)
+ {
+ pMod2 = pMod->papPrereqs[i];
+ if (pMod2)
+ {
+ pMod->papPrereqs[i] = NULL;
+
+ /* 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->cDepRefs && !pMod2->cDynRefs;
+ }
+ }
+
+ /*
+ * 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)
+{
+ KU32 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->cDepRefs
+ || pMod->cDynRefs
+ || pMod->enmState >= KLDRSTATE_PENDING_TERMINATION
+ || pMod->enmState < KLDRSTATE_LOADED_PREREQUISITES)
+ continue;
+ cToUnload += kldrDyldModUnloadPrerequisitesOne(pMod);
+ }
+ }
+}
+
+
+/**
+ * Loads the prerequisite modules this module depends on.
+ *
+ * To find each of the prerequisite modules this method calls
+ * kldrDyldGetPrerequisite() and it will make sure the modules
+ * are added to the load stack frame.
+ *
+ * @returns 0 on success, non-zero native OS or kLdr status code on failure.
+ * The state is changed to LOADED_PREREQUISITES or RELOADED_LOADED_PREREQUISITES.
+ * @param pMod The module.
+ * @param pszPrefix Prefix to use when searching.
+ * @param pszSuffix 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.
+ */
+int kldrDyldModLoadPrerequisites(PKLDRDYLDMOD pMod, const char *pszPrefix, const char *pszSuffix,
+ KLDRDYLDSEARCH enmSearch, unsigned fFlags)
+{
+ KI32 cPrereqs;
+ KU32 i;
+ int rc = 0;
+
+ /* sanity */
+ switch (pMod->enmState)
+ {
+ case KLDRSTATE_MAPPED:
+ case KLDRSTATE_RELOADED:
+ break;
+ default:
+ KLDRDYLDMOD_ASSERT(!"invalid state");
+ return -1;
+ }
+
+ /*
+ * Query number of prerequiste modules and allocate the array.
+ */
+ cPrereqs = kLdrModNumberOfImports(pMod->pMod, NULL);
+ kHlpAssert(cPrereqs >= 0);
+ if (pMod->cPrereqs != cPrereqs)
+ {
+ KLDRDYLDMOD_ASSERT(!pMod->papPrereqs);
+ pMod->papPrereqs = (PPKLDRDYLDMOD)kHlpAllocZ(sizeof(pMod->papPrereqs[0]) * cPrereqs);
+ if (!pMod->papPrereqs)
+ return KERR_NO_MEMORY;
+ pMod->cPrereqs = cPrereqs;
+ }
+ else
+ KLDRDYLDMOD_ASSERT(pMod->papPrereqs || !pMod->cPrereqs);
+
+ /*
+ * Iterate the prerequisites and load them.
+ */
+ for (i = 0; i < pMod->cPrereqs; i++)
+ {
+ static char s_szPrereq[260];
+ PKLDRDYLDMOD pPrereqMod;
+
+ KLDRDYLDMOD_ASSERT(pMod->papPrereqs[i] == NULL);
+ rc = kLdrModGetImport(pMod->pMod, NULL, i, s_szPrereq, sizeof(s_szPrereq));
+ if (rc)
+ break;
+ rc = kldrDyldGetPrerequisite(s_szPrereq, pszPrefix, pszSuffix, enmSearch, fFlags, pMod, &pPrereqMod);
+ if (rc)
+ break;
+ pMod->papPrereqs[i] = pPrereqMod;
+ }
+
+ /* change the state regardless of what happend. */
+ if (pMod->enmState == KLDRSTATE_MAPPED)
+ pMod->enmState = KLDRSTATE_LOADED_PREREQUISITES;
+ else
+ pMod->enmState = KLDRSTATE_RELOADED_LOADED_PREREQUISITES;
+ return rc;
+}
+
+
+/**
+ * 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->enmState == KLDRSTATE_OPEN);
+ KLDRDYLDMOD_ASSERT(!pMod->fMapped);
+ if (pMod->fMapped)
+ return 0;
+
+ /* do the job. */
+ rc = kLdrModMap(pMod->pMod);
+ if (!rc)
+ {
+ rc = kLdrModAllocTLS(pMod->pMod, KLDRMOD_INT_MAP);
+ if (!rc)
+ {
+ /** @todo TLS */
+ pMod->fMapped = 1;
+ pMod->enmState = KLDRSTATE_MAPPED;
+ }
+ else
+ kLdrModUnmap(pMod->pMod);
+ }
+ 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. */
+ if (pMod->fAllocatedTLS)
+ {
+ kLdrModFreeTLS(pMod->pMod, KLDRMOD_INT_MAP);
+ pMod->fAllocatedTLS = 0;
+ }
+ rc = kLdrModUnmap(pMod->pMod);
+ if (!rc)
+ {
+ pMod->fMapped = 0;
+ if (pMod->enmState < KLDRSTATE_PENDING_DESTROY)
+ {
+ pMod->enmState = KLDRSTATE_PENDING_DESTROY;
+ kldrDyldModUnlink(pMod);
+ }
+ }
+
+ return rc;
+}
+
+
+/**
+ * Reloads the module.
+ *
+ * Reloading means that all modified pages are restored to their original
+ * state. Whether this includes the code segments depends on whether the fixups
+ * depend on the addend in the place they are fixing up - so it's format specific.
+ *
+ * @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 kldrDyldModReload(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;
+ }
+
+ /* Free TLS before reloading. */
+ if (pMod->fAllocatedTLS)
+ {
+ kLdrModFreeTLS(pMod->pMod, KLDRMOD_INT_MAP);
+ pMod->fAllocatedTLS = 0;
+ }
+
+ /* Let the module interpreter do the reloading of the mapping. */
+ rc = kLdrModReload(pMod->pMod);
+ if (!rc)
+ {
+ rc = kLdrModAllocTLS(pMod->pMod, KLDRMOD_INT_MAP);
+ if (!rc)
+ {
+ pMod->fAllocatedTLS = 1;
+ pMod->enmState = KLDRSTATE_RELOADED;
+ }
+ }
+ return rc;
+}
+
+
+/**
+ * @copydoc FNKLDRMODGETIMPORT
+ * pvUser points to the KLDRDYLDMOD.
+ */
+static int kldrDyldModFixupGetImportCallback(PKLDRMOD pMod, KU32 iImport, KU32 iSymbol,
+ const char *pchSymbol, KSIZE cchSymbol, const char *pszVersion,
+ PKLDRADDR puValue, KU32 *pfKind, void *pvUser)
+{
+ static int s_cRecursiveCalls = 0;
+ PKLDRDYLDMOD pDyldMod = (PKLDRDYLDMOD)pvUser;
+ int rc;
+
+ /* guard against too deep forwarder recursion. */
+ if (s_cRecursiveCalls >= 5)
+ return KLDR_ERR_TOO_LONG_FORWARDER_CHAIN;
+ s_cRecursiveCalls++;
+
+ if (iImport != NIL_KLDRMOD_IMPORT)
+ {
+ /* specific import module search. */
+ PKLDRDYLDMOD pPrereqMod;
+
+ KLDRDYLDMOD_ASSERT(iImport < pDyldMod->cPrereqs);
+ pPrereqMod = pDyldMod->papPrereqs[iImport];
+
+ KLDRDYLDMOD_ASSERT(pPrereqMod);
+ KLDRDYLDMOD_ASSERT(pPrereqMod->u32MagicHead == KLDRDYMOD_MAGIC);
+ KLDRDYLDMOD_ASSERT(pPrereqMod->u32MagicTail == KLDRDYMOD_MAGIC);
+ KLDRDYLDMOD_ASSERT(pPrereqMod->enmState < KLDRSTATE_TERMINATING);
+
+ rc = kLdrModQuerySymbol(pPrereqMod->pMod, NULL, KLDRMOD_BASEADDRESS_MAP,
+ iSymbol, pchSymbol, cchSymbol, pszVersion,
+ kldrDyldModFixupGetImportCallback, pPrereqMod, puValue, pfKind);
+ if (rc)
+ {
+ if (pchSymbol)
+ kldrDyldFailure(rc, "%s[%d]->%s.%.*s%s", pDyldMod->pMod->pszName, iImport,
+ pPrereqMod->pMod->pszName, cchSymbol, pchSymbol, pszVersion ? pszVersion : "");
+ else
+ kldrDyldFailure(rc, "%s[%d]->%s.%d%s", pDyldMod->pMod->pszName, iImport,
+ pPrereqMod->pMod->pszName, iSymbol, pszVersion ? pszVersion : "");
+ }
+ }
+ else
+ {
+ /* bind list search. */
+ unsigned fFound = 0;
+ PKLDRDYLDMOD pBindMod = g_pkLdrDyldBindHead;
+ rc = 0;
+ while (pBindMod)
+ {
+ KU32 fKind;
+ KLDRADDR uValue;
+ rc = kLdrModQuerySymbol(pBindMod->pMod, NULL, KLDRMOD_BASEADDRESS_MAP,
+ iSymbol, pchSymbol, cchSymbol, pszVersion,
+ kldrDyldModFixupGetImportCallback, pBindMod, &uValue, &fKind);
+ if ( !rc
+ && ( !fFound
+ || !(fKind & KLDRSYMKIND_WEAK)
+ )
+ )
+ {
+ *pfKind = fKind;
+ *puValue = uValue;
+ fFound = 1;
+ if (!(fKind & KLDRSYMKIND_WEAK))
+ break;
+ }
+
+ /* next */
+ pBindMod = pBindMod->Bind.pNext;
+ }
+ rc = fFound ? 0 : KLDR_ERR_SYMBOL_NOT_FOUND;
+ if (!fFound)
+ {
+ if (pchSymbol)
+ kldrDyldFailure(rc, "%s->%.*s%s", pDyldMod->pMod->pszName, cchSymbol, pchSymbol, pszVersion ? pszVersion : "");
+ else
+ kldrDyldFailure(rc, "%s->%d%s", pDyldMod->pMod->pszName, iSymbol, pszVersion ? pszVersion : "");
+ }
+ }
+
+ s_cRecursiveCalls--;
+ 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, kldrDyldModFixupGetImportCallback, pMod);/** @todo fixme. */
+ if (!rc)
+ pMod->enmState = KLDRSTATE_FIXED_UP;
+ return rc;
+}
+
+
+/**
+ * Calls the module initialization entry point if any.
+ *
+ * This is considered to be a module specific thing and leave if
+ * to the module interpreter. They will have to deal with different
+ * module init practices between platforms should there be any.
+ *
+ * @returns 0 and state changed to GOOD on success.
+ * Non-zero OS or kLdr status code and status changed to INITIALIZATION_FAILED on failure.
+ * @param pMod The module that should be initialized.
+ */
+int kldrDyldModCallInit(PKLDRDYLDMOD pMod)
+{
+ int rc;
+
+ KLDRDYLDMOD_ASSERT(pMod->enmState == KLDRSTATE_PENDING_INITIALIZATION);
+ KLDRDYLDMOD_ASSERT(!pMod->fInitList);
+
+ pMod->enmState = KLDRSTATE_INITIALIZING;
+ rc = kLdrModCallInit(pMod->pMod, KLDRMOD_INT_MAP, (KUPTR)pMod->hMod);
+ if (!rc)
+ {
+ pMod->enmState = KLDRSTATE_GOOD;
+ /* push it onto the termination list.*/
+ pMod->InitTerm.pPrev = NULL;
+ pMod->InitTerm.pNext = g_pkLdrDyldTermHead;
+ if (g_pkLdrDyldTermHead)
+ g_pkLdrDyldTermHead->InitTerm.pPrev = pMod;
+ else
+ g_pkLdrDyldTermTail = pMod;
+ g_pkLdrDyldTermHead = pMod;
+ }
+ else
+ pMod->enmState = KLDRSTATE_INITIALIZATION_FAILED;
+
+ return rc;
+}
+
+
+/**
+ * Calls the module termination entry point if any.
+ *
+ * This'll change the module status to PENDING_GC.
+ *
+ * @param pMod The module that should be initialized.
+ */
+void kldrDyldModCallTerm(PKLDRDYLDMOD pMod)
+{
+ KLDRDYLDMOD_ASSERT(pMod->enmState == KLDRSTATE_PENDING_TERMINATION);
+
+ pMod->enmState = KLDRSTATE_TERMINATING;
+ kLdrModCallTerm(pMod->pMod, KLDRMOD_INT_MAP, (KUPTR)pMod->hMod);
+ pMod->enmState = KLDRSTATE_PENDING_GC;
+ /* unlinking on destruction. */
+}
+
+
+/**
+ * Calls the thread attach entry point if any.
+ *
+ * @returns 0 on success, non-zero on failure.
+ * @param pMod The module.
+ */
+int kldrDyldModAttachThread(PKLDRDYLDMOD pMod)
+{
+ KLDRDYLDMOD_ASSERT(pMod->enmState == KLDRSTATE_GOOD);
+
+ return kLdrModCallThread(pMod->pMod, KLDRMOD_INT_MAP, (KUPTR)pMod->hMod, 1 /* attach */);
+}
+
+
+/**
+ * Calls the thread detach entry point if any.
+ *
+ * @returns 0 on success, non-zero on failure.
+ * @param pMod The module.
+ */
+void kldrDyldModDetachThread(PKLDRDYLDMOD pMod)
+{
+ KLDRDYLDMOD_ASSERT(pMod->enmState == KLDRSTATE_GOOD);
+
+ kLdrModCallThread(pMod->pMod, KLDRMOD_INT_MAP, (KUPTR)pMod->hMod, 0 /* detach */);
+}
+
+
+/**
+ * Gets the main stack, allocate it if necessary.
+ *
+ * @returns 0 on success, non-zero native OS or kLdr status code on failure.
+ * @param pMod The module.
+ * @param ppvStack Where to store the address of the stack (lowest address).
+ * @param pcbStack Where to store the size of the stack.
+ */
+int kldrDyldModGetMainStack(PKLDRDYLDMOD pMod, void **ppvStack, KSIZE *pcbStack)
+{
+ int rc = 0;
+ KLDRSTACKINFO StackInfo;
+ KLDRDYLDMOD_ASSERT(pMod->fExecutable);
+
+ /*
+ * Since we might have to allocate the stack ourselves, and there will only
+ * ever be one main stack, we'll be keeping the main stack info in globals.
+ */
+ if (!g_fkLdrDyldDoneMainStack)
+ {
+ rc = kLdrModGetStackInfo(pMod->pMod, NULL, KLDRMOD_BASEADDRESS_MAP, &StackInfo);
+ if (!rc)
+ {
+ /* check if there is a stack size override/default. */
+ KSIZE cbDefOverride;
+ if (kHlpGetEnvUZ("KLDR_MAIN_STACK_SIZE", &cbDefOverride))
+ cbDefOverride = 0;
+
+
+ /* needs allocating? */
+ if ( StackInfo.LinkAddress == NIL_KLDRADDR
+ || StackInfo.cbStack < cbDefOverride)
+ {
+ KSIZE cbStack = (KSIZE)K_MAX(StackInfo.cbStack, cbDefOverride);
+
+ g_pvkLdrDyldMainStack = kldrDyldOSAllocStack(cbStack);
+ if (g_pvkLdrDyldMainStack)
+ {
+ g_cbkLdrDyldMainStack = cbStack;
+ g_fkLdrDyldMainStackAllocated = 1;
+ }
+ else
+ rc = KLDR_ERR_MAIN_STACK_ALLOC_FAILED;
+ }
+ else
+ {
+ KLDRDYLDMOD_ASSERT(StackInfo.Address != NIL_KLDRADDR);
+ KLDRDYLDMOD_ASSERT(StackInfo.cbStack > 0);
+
+ g_fkLdrDyldMainStackAllocated = 0;
+ g_pvkLdrDyldMainStack = (void *)(KUPTR)StackInfo.Address;
+ KLDRDYLDMOD_ASSERT((KUPTR)g_pvkLdrDyldMainStack == StackInfo.Address);
+
+ g_cbkLdrDyldMainStack = (KSIZE)StackInfo.cbStack;
+ KLDRDYLDMOD_ASSERT(StackInfo.cbStack == g_cbkLdrDyldMainStack);
+ }
+ }
+ if (!rc)
+ g_fkLdrDyldDoneMainStack = 1;
+ }
+
+ if (!rc)
+ {
+ if (ppvStack)
+ *ppvStack = g_pvkLdrDyldMainStack;
+ if (pcbStack)
+ *pcbStack = g_cbkLdrDyldMainStack;
+ }
+
+ return rc;
+}
+
+
+/**
+ * This starts the executable module.
+ *
+ * @returns non-zero OS or kLdr status code on failure.
+ * (won't return on success.)
+ * @param pMod The executable module.
+ */
+int kldrDyldModStartExe(PKLDRDYLDMOD pMod)
+{
+ int rc;
+ KLDRADDR MainEPAddress;
+ void *pvStack;
+ KSIZE cbStack;
+ KLDRDYLDMOD_ASSERT(pMod->fExecutable);
+
+ rc = kLdrModQueryMainEntrypoint(pMod->pMod, NULL, KLDRMOD_BASEADDRESS_MAP, &MainEPAddress);
+ if (rc)
+ return rc;
+ rc = kldrDyldModGetMainStack(pMod, &pvStack, &cbStack);
+ if (rc)
+ return rc;
+ return kldrDyldOSStartExe((KUPTR)MainEPAddress, pvStack, cbStack);
+}
+
+
+/**
+ * Gets the module name.
+ *
+ * @returns 0 on success, KERR_BUFFER_OVERFLOW on failure.
+ * @param pMod The module.
+ * @param pszName Where to store the name.
+ * @param cchName The size of the name buffer.
+ */
+int kldrDyldModGetName(PKLDRDYLDMOD pMod, char *pszName, KSIZE cchName)
+{
+ KSIZE cch = K_MIN(cchName, pMod->pMod->cchName + 1);
+ if (cch)
+ {
+ kHlpMemCopy(pszName, pMod->pMod->pszName, cch - 1);
+ pszName[cch - 1] = '\0';
+ }
+ return cchName <= pMod->pMod->cchName ? KERR_BUFFER_OVERFLOW : 0;
+}
+
+
+/**
+ * Gets the module filename.
+ *
+ * @returns 0 on success, KERR_BUFFER_OVERFLOW on failure.
+ * @param pMod The module.
+ * @param pszFilename Where to store the filename.
+ * @param cchFilename The size of the filename buffer.
+ */
+int kldrDyldModGetFilename(PKLDRDYLDMOD pMod, char *pszFilename, KSIZE cchFilename)
+{
+ KSIZE cch = K_MIN(cchFilename, pMod->pMod->cchFilename + 1);
+ if (cch)
+ {
+ kHlpMemCopy(pszFilename, pMod->pMod->pszFilename, cch - 1);
+ pszFilename[cch - 1] = '\0';
+ }
+ return cchFilename <= pMod->pMod->cchFilename ? KERR_BUFFER_OVERFLOW : 0;
+}
+
+
+/**
+ * Gets the address/value of a symbol in the specified module.
+ *
+ * @returns 0 on success, KLDR_ERR_SYMBOL_NOT_FOUND on failure.
+ * @param pMod The module.
+ * @param uSymbolOrdinal The symbol ordinal 0. This is ignored if the name is non-zero.
+ * @param pszSymbolName The symbol name. Can be NULL.
+ * @param puValue Where to store the value. optional.
+ * @param pfKind Where to store the symbol kind. optional.
+ */
+int kldrDyldModQuerySymbol(PKLDRDYLDMOD pMod, KU32 uSymbolOrdinal, const char *pszSymbolName,
+ KUPTR *puValue, KU32 *pfKind)
+{
+ int rc;
+ KLDRADDR uValue = 0;
+ KU32 fKind = 0;
+
+ rc = kLdrModQuerySymbol(pMod->pMod, NULL, KLDRMOD_BASEADDRESS_MAP,
+ uSymbolOrdinal, pszSymbolName, kHlpStrLen(pszSymbolName), NULL,
+ kldrDyldModFixupGetImportCallback, pMod,
+ &uValue, &fKind);
+ if (!rc)
+ {
+ if (puValue)
+ {
+ *puValue = (KUPTR)uValue;
+ KLDRDYLDMOD_ASSERT(*puValue == uValue);
+ }
+ if (pfKind)
+ *pfKind = fKind;
+ }
+
+ return rc;
+}
+