diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:21:29 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:21:29 +0000 |
commit | 29cd838eab01ed7110f3ccb2e8c6a35c8a31dbcc (patch) | |
tree | 63ef546b10a81d461e5cf5ed9e98a68cd7dee1aa /src/lib/kStuff/kLdr/kLdrDyld.c | |
parent | Initial commit. (diff) | |
download | kbuild-29cd838eab01ed7110f3ccb2e8c6a35c8a31dbcc.tar.xz kbuild-29cd838eab01ed7110f3ccb2e8c6a35c8a31dbcc.zip |
Adding upstream version 1:0.1.9998svn3589+dfsg.upstream/1%0.1.9998svn3589+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | src/lib/kStuff/kLdr/kLdrDyld.c | 1509 |
1 files changed, 1509 insertions, 0 deletions
diff --git a/src/lib/kStuff/kLdr/kLdrDyld.c b/src/lib/kStuff/kLdr/kLdrDyld.c new file mode 100644 index 0000000..9ff3dd8 --- /dev/null +++ b/src/lib/kStuff/kLdr/kLdrDyld.c @@ -0,0 +1,1509 @@ +/* $Id: kLdrDyld.c 29 2009-07-01 20:30:29Z bird $ */ +/** @file + * kLdr - The Dynamic Loader. + */ + +/* + * 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 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) kHlpAssert(expr) +#else +# define KLDRDYLD_ASSERT(expr) do {} while (0) +#endif + + +/******************************************************************************* +* Structures and Typedefs * +*******************************************************************************/ + + +/******************************************************************************* +* Global Variables * +*******************************************************************************/ +/** Pointer to the executable module. + * (This is exported, so no prefix.) */ +PKLDRDYLDMOD kLdrDyldExe = NULL; +/** 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 initialization list. + * The outermost load call will pop elements from this list in LIFO order (i.e. + * from the tail). The list is only used during non-recursive initialization + * and may therefore share the pNext/pPrev members with the termination list + * since we don't push a module onto the termination list untill it has been + * successfully initialized. */ +PKLDRDYLDMOD g_pkLdrDyldInitHead; +/** Pointer to the tail module of the initalization list.*/ +PKLDRDYLDMOD g_pkLdrDyldInitTail; +/** Pointer to the head module of the termination order list. + * This is a LIFO just like the the init 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; + +/** Flag indicating bootstrap time. + * When set the error behaviour changes. Any kind of serious failure + * is fatal and will terminate the process. */ +int g_fBootstrapping; +/** The global error buffer. */ +char g_szkLdrDyldError[1024]; + +/** The default flags. */ +KU32 kLdrDyldFlags = 0; +/** The default search method. */ +KLDRDYLDSEARCH kLdrDyldSearch = KLDRDYLD_SEARCH_HOST; + + +/** @name The main stack. + * @{ */ +/** Indicates that the other MainStack globals have been filled in. */ +unsigned g_fkLdrDyldDoneMainStack = 0; +/** Whether the stack was allocated seperatly or was part of the executable. */ +unsigned g_fkLdrDyldMainStackAllocated = 0; +/** Pointer to the main stack object. */ +void *g_pvkLdrDyldMainStack = NULL; +/** The size of the main stack object. */ +KSIZE g_cbkLdrDyldMainStack = 0; +/** @} */ + + +/** The load stack. + * This contains frames with modules affected by active loads. + * + * Each kLdrDyldLoad and kLdrDyldLoadExe call will create a new stack frame containing + * all the modules involved in the operation. The modules will be ordered in recursive + * init order within the frame. + */ +static PPKLDRDYLDMOD g_papStackMods; +/** The number of used entries in the g_papStackMods array. */ +static KU32 g_cStackMods; +/** The number of entries allocated for the g_papStackMods array. */ +static KU32 g_cStackModsAllocated; +/** Number of active load calls. */ +static KU32 g_cActiveLoadCalls; +/** Number of active unload calls. */ +static KU32 g_cActiveUnloadCalls; +/** Total number of load calls. */ +static KU32 g_cTotalLoadCalls; +/** Total mumber of unload calls. */ +static KU32 g_cTotalUnloadCalls; +/** Boolean flag indicating that GC is active. */ +static KU32 g_fActiveGC; + + + +/******************************************************************************* +* Internal Functions * +*******************************************************************************/ +/** @name API worker routines. + * @internal + * @{ */ +void kldrDyldDoLoadExeStackSwitch(PKLDRDYLDMOD pExe, void *pvStack, KSIZE cbStack); +static int kldrDyldDoLoad(const char *pszDll, const char *pszPrefix, const char *pszSuffix, KLDRDYLDSEARCH enmSearch, + unsigned fFlags, PPKLDRDYLDMOD ppMod, char *pszErr, KSIZE cchErr); +static int kldrDyldDoLoad2(PKLDRDYLDMOD pLoadedMod, const char *pszPrefix, const char *pszSuffix, + KLDRDYLDSEARCH enmSearch, unsigned fFlags); +static int kldrDyldDoLoadPrerequisites(PKLDRDYLDMOD pMod, const char *pszPrefix, const char *pszSuffix, + KLDRDYLDSEARCH enmSearch, unsigned fFlags); +static int kldrDyldDoUnload(PKLDRDYLDMOD pMod); +static int kldrDyldDoFindByName(const char *pszDll, const char *pszPrefix, const char *pszSuffix, KLDRDYLDSEARCH enmSearch, + unsigned fFlags, PPKLDRDYLDMOD ppMod); +static int kldrDyldDoFindByAddress(KUPTR Address, PPKLDRDYLDMOD ppMod, KU32 *piSegment, KUPTR *poffSegment); +static int kldrDyldDoGetName(PKLDRDYLDMOD pMod, char *pszName, KSIZE cchName); +static int kldrDyldDoGetFilename(PKLDRDYLDMOD pMod, char *pszFilename, KSIZE cchFilename); +static int kldrDyldDoQuerySymbol(PKLDRDYLDMOD pMod, KU32 uSymbolOrdinal, const char *pszSymbolName, KUPTR *pValue, KU32 *pfKind); +/** @} */ + +/** @name Misc load/unload workers + * @internal + * @{ + */ +static void kldrDyldDoModuleTerminationAndGarabageCollection(void); +/** @} */ + +/** @name The load stack. + * @internal + * @{ */ +static KU32 kldrDyldStackNewFrame(PKLDRDYLDMOD pMod); +static int kldrDyldStackAddModule(PKLDRDYLDMOD pMod); +static int kldrDyldStackFrameCompleted(void); +static void kldrDyldStackCleanupOne(PKLDRDYLDMOD pMod, int rc); +static void kldrDyldStackDropFrame(KU32 iLoad1st, KU32 iLoadEnd, int rc); +/** @} */ + +static int kldrDyldCopyError(int rc, char *pszErr, KSIZE cchErr); + + + +/** + * Initialize the dynamic loader. + */ +int kldrDyldInit(void) +{ + kLdrDyldHead = kLdrDyldTail = NULL; + g_pkLdrDyldTermHead = g_pkLdrDyldTermTail = NULL; + g_pkLdrDyldBindHead = g_pkLdrDyldBindTail = NULL; + kLdrDyldFlags = 0; + g_szkLdrDyldError[0] = '\0'; + + g_fkLdrDyldDoneMainStack = 0; + g_fkLdrDyldMainStackAllocated = 0; + g_pvkLdrDyldMainStack = NULL; + g_cbkLdrDyldMainStack = 0; + + return kldrDyldFindInit(); +} + + +/** + * Terminate the dynamic loader. + */ +void kldrDyldTerm(void) +{ + +} + + +/** + * Bootstrap an executable. + * + * This is called from the executable stub to replace the stub and run the + * executable specified in the argument package. + * + * Since this is boostrap time there isn't anything to return to. So, instead + * the process will be terminated upon failure. + * + * We also have to keep in mind that this function is called on a small, small, + * stack and therefore any kind of large stack objects or deep recursions must + * be avoided. Since loading the executable will involve more or less all + * operations in the loader, this restriction really applies everywhere. + * + * @param pArgs Pointer to the argument package residing in the executable stub. + * @param pvOS OS specific argument. + */ +#ifndef __OS2__ /* kLdrDyldLoadExe is implemented in assembly on OS/2. */ +void kLdrDyldLoadExe(PCKLDREXEARGS pArgs, void *pvOS) +#else +void kldrDyldLoadExe(PCKLDREXEARGS pArgs, void *pvOS) +#endif +{ + void *pvStack; + KSIZE cbStack; + PKLDRDYLDMOD pExe; + int rc; + + /* + * Indicate that we're boostrapping and ensure that initialization was successful. + */ + g_fBootstrapping = 1; + rc = kldrInit(); + if (rc) + kldrDyldFailure(rc, "Init failure, rc=%d", rc); + + /* + * Validate the argument package. + */ + if (pArgs->fFlags & ~( KLDRYDLD_LOAD_FLAGS_GLOBAL_SYMBOLS + | KLDRYDLD_LOAD_FLAGS_DEEP_SYMBOLS + | KLDRDYLD_LOAD_FLAGS_RECURSIVE_INIT + | KLDRYDLD_LOAD_FLAGS_SPECIFIC_MODULE)) + kldrDyldFailure(KERR_INVALID_PARAMETER, "Bad fFlags=%#x", pArgs->fFlags); + if ( pArgs->enmSearch <= KLDRDYLD_SEARCH_INVALID + || pArgs->enmSearch >= KLDRDYLD_SEARCH_END) + kldrDyldFailure(KERR_INVALID_PARAMETER, "Bad enmSearch=%d", pArgs->enmSearch); + + /* + * Set defaults. + */ + kLdrDyldFlags |= (pArgs->fFlags & KLDRDYLD_LOAD_FLAGS_RECURSIVE_INIT); + kLdrDyldSearch = pArgs->enmSearch; + + /** @todo make sense of this default prefix/suffix stuff. */ + if (pArgs->szDefPrefix[0] != '\0') + kHlpMemCopy(kLdrDyldDefPrefix, pArgs->szDefPrefix, K_MIN(sizeof(pArgs->szDefPrefix), sizeof(kLdrDyldDefPrefix))); + if (pArgs->szDefSuffix[0] != '\0') + kHlpMemCopy(kLdrDyldDefSuffix, pArgs->szDefSuffix, K_MIN(sizeof(pArgs->szDefSuffix), sizeof(kLdrDyldDefSuffix))); + + /** @todo append that path to the one for the specified search method. */ + /** @todo create a function for doing this, an exposed api preferably. */ + /* append path */ + cbStack = sizeof(kLdrDyldLibraryPath) - kHlpStrLen(kLdrDyldLibraryPath); /* borrow cbStack for a itty bit. */ + kHlpMemCopy(kLdrDyldLibraryPath, pArgs->szLibPath, K_MIN(sizeof(pArgs->szLibPath), cbStack)); + kLdrDyldLibraryPath[sizeof(kLdrDyldLibraryPath) - 1] = '\0'; + + /* + * Make sure we own the loader semaphore (necessary for init). + */ + rc = kLdrDyldSemRequest(); + if (rc) + kldrDyldFailure(rc, "Sem req. failure, rc=%d", rc); + + /* + * Open and map the executable module before we join paths with kLdrDyldLoad(). + */ + rc = kldrDyldFindNewModule(pArgs->szExecutable, NULL, NULL, pArgs->enmSearch, + pArgs->fFlags | KLDRDYLD_LOAD_FLAGS_EXECUTABLE, &pExe); + if (rc) + kldrDyldFailure(rc, "Can't find/open the executable '%s', rc=%d", pArgs->szExecutable, rc); + rc = kldrDyldModMap(pExe); + if (rc) + kldrDyldFailure(rc, "Failed to map the executable '%s', rc=%d", pExe->pMod->pszFilename, rc); + + kLdrDyldExe = pExe; + + /* + * Query the stack and go to OS specific code to + * setup and switch stack. The OS specific code will call us + * back at kldrDyldDoLoadExe. + */ + rc = kldrDyldModGetMainStack(pExe, &pvStack, &cbStack); + if (rc) + kldrDyldFailure(rc, "Failed to map the executable '%s', rc=%d", pExe->pMod->pszFilename, rc); + kldrDyldDoLoadExeStackSwitch(pExe, pvStack, cbStack); + kldrDyldFailure(-1, "Failed to setup the stack for '%s'.", pExe->pMod->pszFilename); +} + + +/** + * 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 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. + * @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 *pszPrefix, const char *pszSuffix, KLDRDYLDSEARCH enmSearch, + unsigned fFlags, PHKLDRMOD phMod, char *pszErr, KSIZE cchErr) +{ + int rc; + + /* validate arguments and initialize return values. */ + if (pszErr && cchErr) + *pszErr = '\0'; + *phMod = NIL_HKLDRMOD; + K_VALIDATE_STRING(pszDll); + K_VALIDATE_OPTIONAL_STRING(pszPrefix); + K_VALIDATE_OPTIONAL_STRING(pszSuffix); + K_VALIDATE_ENUM(enmSearch, KLDRDYLD_SEARCH); + K_VALIDATE_OPTIONAL_BUFFER(pszErr, cchErr); + + /* get the semaphore and do the job. */ + rc = kLdrDyldSemRequest(); + if (!rc) + { + PKLDRDYLDMOD pMod = NULL; + g_cTotalLoadCalls++; + g_cActiveLoadCalls++; + rc = kldrDyldDoLoad(pszDll, pszPrefix, pszSuffix, enmSearch, fFlags, &pMod, pszErr, cchErr); + g_cActiveLoadCalls--; + kldrDyldDoModuleTerminationAndGarabageCollection(); + kLdrDyldSemRelease(); + *phMod = pMod ? pMod->hMod : NIL_HKLDRMOD; + } + 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 = kLdrDyldSemRequest(); + if (!rc) + { + g_cTotalUnloadCalls++; + g_cActiveUnloadCalls++; + rc = kldrDyldDoUnload(hMod); + g_cActiveUnloadCalls--; + kldrDyldDoModuleTerminationAndGarabageCollection(); + kLdrDyldSemRelease(); + } + 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 pszPrefix Prefix than can be used when searching. + * @param pszSuffix 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 *pszPrefix, const char *pszSuffix, KLDRDYLDSEARCH enmSearch, + unsigned fFlags, PHKLDRMOD phMod) +{ + int rc; + + /* validate & initialize */ + *phMod = NIL_HKLDRMOD; + K_VALIDATE_STRING(pszDll); + + /* get sem & do work */ + rc = kLdrDyldSemRequest(); + if (!rc) + { + PKLDRDYLDMOD pMod = NULL; + rc = kldrDyldDoFindByName(pszDll, pszPrefix, pszSuffix, enmSearch, fFlags, &pMod); + kLdrDyldSemRelease(); + *phMod = pMod ? pMod->hMod : NIL_HKLDRMOD; + } + 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(KUPTR Address, PHKLDRMOD phMod, KU32 *piSegment, KUPTR *poffSegment) +{ + int rc; + + /* validate & initialize */ + *phMod = NIL_HKLDRMOD; + if (piSegment) + *piSegment = ~(KU32)0; + if (poffSegment) + *poffSegment = ~(KUPTR)0; + + /* get sem & do work */ + rc = kLdrDyldSemRequest(); + if (!rc) + { + PKLDRDYLDMOD pMod = NULL; + rc = kldrDyldDoFindByAddress(Address, &pMod, piSegment, poffSegment); + kLdrDyldSemRelease(); + *phMod = pMod ? pMod->hMod : NIL_HKLDRMOD; + } + return rc; +} + + +/** + * Gets the module name. + * + * @returns 0 on success and pszName filled with the name. + * @returns KERR_INVALID_HANDLE or KERR_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, KSIZE cchName) +{ + int rc; + + /* validate */ + if (pszName && cchName) + *pszName = '\0'; + KLDRDYLD_VALIDATE_HKLDRMOD(hMod); + K_VALIDATE_BUFFER(pszName, cchName); + + /* get sem & do work */ + rc = kLdrDyldSemRequest(); + if (!rc) + { + rc = kldrDyldDoGetName(hMod, pszName, cchName); + kLdrDyldSemRelease(); + } + return rc; +} + + +/** + * Gets the module filename. + * + * @returns 0 on success and pszFilename filled with the name. + * @returns KERR_INVALID_HANDLE or KERR_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, KSIZE cchFilename) +{ + int rc; + + /* validate & initialize */ + if (pszFilename && cchFilename); + *pszFilename = '\0'; + KLDRDYLD_VALIDATE_HKLDRMOD(hMod); + K_VALIDATE_BUFFER(pszFilename, cchFilename); + + /* get sem & do work */ + rc = kLdrDyldSemRequest(); + if (!rc) + { + rc = kldrDyldDoGetFilename(hMod, pszFilename, cchFilename); + kLdrDyldSemRelease(); + } + return rc; +} + + +/** + * Queries the value and type of a symbol. + * + * @returns 0 on success and pValue and pfKind set. + * @returns KERR_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 pszSymbolVersion The symbol version. Optional. + * @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, KU32 uSymbolOrdinal, const char *pszSymbolName, + const char *pszSymbolVersion, KUPTR *pValue, KU32 *pfKind) +{ + int rc; + + /* validate & initialize */ + if (pfKind) + *pfKind = 0; + if (pValue) + *pValue = 0; + if (!pfKind && !pValue) + return KERR_INVALID_PARAMETER; + KLDRDYLD_VALIDATE_HKLDRMOD(hMod); + K_VALIDATE_OPTIONAL_STRING(pszSymbolName); + + /* get sem & do work */ + rc = kLdrDyldSemRequest(); + if (!rc) + { + rc = kldrDyldDoQuerySymbol(hMod, uSymbolOrdinal, pszSymbolName, pValue, pfKind); + kLdrDyldSemRelease(); + } + return rc; +} + + +/** + * Worker kLdrDoLoadExe(). + * Used after we've switch to the final process stack. + * + * @param pExe The executable module. + * @internal + */ +void kldrDyldDoLoadExe(PKLDRDYLDMOD pExe) +{ + int rc; + + /* + * Load the executable module with its prerequisites and initialize them. + */ + g_cActiveLoadCalls++; + rc = kldrDyldDoLoad2(pExe, NULL, NULL, kLdrDyldSearch, kLdrDyldFlags | KLDRDYLD_LOAD_FLAGS_EXECUTABLE); + if (rc) + kldrDyldFailure(rc, "load 2 failed for '%s', rc=%d", pExe->pMod->pszFilename); + g_cActiveLoadCalls--; + kldrDyldDoModuleTerminationAndGarabageCollection(); + + /* + * Invoke the executable entry point. + */ + kldrDyldModStartExe(pExe); + kldrDyldFailure(-1, "failed to invoke main!"); +} + + +/** + * Worker for kLdrDyldLoad() and helper for kLdrDyldLoadExe(). + * @internal + */ +static int kldrDyldDoLoad(const char *pszDll, const char *pszPrefix, const char *pszSuffix, KLDRDYLDSEARCH enmSearch, + unsigned fFlags, PPKLDRDYLDMOD ppMod, char *pszErr, KSIZE cchErr) +{ + int rc; + + /* + * Try find the module among the ones that's already loaded. + */ + rc = kldrDyldFindExistingModule(pszDll, pszPrefix, pszSuffix, enmSearch, fFlags, ppMod); + if (!rc) + { + switch ((*ppMod)->enmState) + { + /* + * Prerequisites are ok, so nothing to do really. + */ + case KLDRSTATE_GOOD: + case KLDRSTATE_INITIALIZING: + return kldrDyldModDynamicLoad(*ppMod); + + /* + * The module can't be loaded because it failed to initialize. + */ + case KLDRSTATE_INITIALIZATION_FAILED: + return KLDR_ERR_MODULE_INIT_FAILED_ALREADY; + + /* + * Prerequisites needs loading / reattaching and the module + * (may depending on fFlags) needs to be initialized. + */ + case KLDRSTATE_PENDING_INITIALIZATION: + break; + + /* + * Prerequisites needs to be loaded again + */ + 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, pszPrefix, pszSuffix, enmSearch, fFlags, ppMod); + if (rc) + return kldrDyldCopyError(rc, pszErr, cchErr); + rc = kldrDyldModMap(*ppMod); + } + + /* + * Join cause with kLdrDyldLoadExe. + */ + if (!rc) + rc = kldrDyldDoLoad2(*ppMod, pszPrefix, pszSuffix, enmSearch, fFlags); + else + kldrDyldStackCleanupOne(*ppMod, rc); + + /* + * Copy any error or warning to the error buffer. + */ + return kldrDyldCopyError(rc, pszErr, cchErr); +} + + +/** + * 2nd half of kLdrDyldLoad() and kLdrDyldLoadExe(). + * + * @internal + */ +static int kldrDyldDoLoad2(PKLDRDYLDMOD pLoadedMod, const char *pszPrefix, const char *pszSuffix, + KLDRDYLDSEARCH enmSearch, unsigned fFlags) +{ + /* + * Load prerequisites. + */ + KU32 i; + KU32 iLoad1st = kldrDyldStackNewFrame(pLoadedMod); + int rc = kldrDyldDoLoadPrerequisites(pLoadedMod, pszPrefix, pszSuffix, enmSearch, fFlags); + KU32 iLoadEnd = kldrDyldStackFrameCompleted(); + if (rc) + { + kldrDyldModAddRef(pLoadedMod); + kldrDyldStackCleanupOne(pLoadedMod, rc); /* in case it didn't get pushed onto the stack. */ + kldrDyldModDeref(pLoadedMod); + } + + /* + * Apply fixups. + */ + for (i = iLoad1st; !rc && i < iLoadEnd; i++) + { + PKLDRDYLDMOD pMod = g_papStackMods[i]; + if ( pMod->enmState == KLDRSTATE_LOADED_PREREQUISITES + || pMod->enmState == KLDRSTATE_RELOADED_LOADED_PREREQUISITES) + rc = kldrDyldModFixup(pMod); + } + + /* + * Advance fixed up module onto initialization. + */ + for (i = iLoad1st; !rc && i < iLoadEnd; i++) + { + PKLDRDYLDMOD pMod = g_papStackMods[i]; + if ( pMod->enmState == KLDRSTATE_FIXED_UP + || pMod->enmState == KLDRSTATE_RELOADED_FIXED_UP) + pMod->enmState = KLDRSTATE_PENDING_INITIALIZATION; + KLDRDYLD_ASSERT( pMod->enmState == KLDRSTATE_PENDING_INITIALIZATION + || pMod->enmState == KLDRSTATE_GOOD); + } + + /* + * Call the initializers if we're loading in recursive mode or + * if we're the outermost load call. + */ + if (fFlags & KLDRDYLD_LOAD_FLAGS_RECURSIVE_INIT) + { + for (i = iLoad1st; !rc && i < iLoadEnd; i++) + { + PKLDRDYLDMOD pMod = g_papStackMods[i]; + if (pMod->enmState == KLDRSTATE_PENDING_INITIALIZATION) + rc = kldrDyldModCallInit(pMod); + else if (pMod->enmState == KLDRSTATE_INITIALIZATION_FAILED) + rc = KLDR_ERR_PREREQUISITE_MODULE_INIT_FAILED_ALREADY; + else + KLDRDYLD_ASSERT(g_papStackMods[i]->enmState == KLDRSTATE_GOOD); + } +#ifdef KLDRDYLD_STRICT + for (i = iLoad1st; !rc && i < iLoadEnd; i++) + KLDRDYLD_ASSERT(g_papStackMods[i]->enmState == KLDRSTATE_GOOD); +#endif + } + else if (g_cActiveLoadCalls <= 1) + { + while (!rc && g_pkLdrDyldInitHead) + { + PKLDRDYLDMOD pMod = g_pkLdrDyldInitHead; + g_pkLdrDyldInitHead = pMod->InitTerm.pNext; + if (pMod->InitTerm.pNext) + pMod->InitTerm.pNext->InitTerm.pPrev = NULL; + else + g_pkLdrDyldInitTail = NULL; + pMod->fInitList = 0; + rc = kldrDyldModCallInit(pMod); + } + } + + /* + * Complete the load by incrementing the dynamic load count of the + * requested module (return handle is already set). + */ + if (!rc) + { + if (fFlags & KLDRDYLD_LOAD_FLAGS_EXECUTABLE) + { + pLoadedMod->cDepRefs++; /* just make it stick. */ + pLoadedMod->cRefs++; + } + else + rc = kldrDyldModDynamicLoad(pLoadedMod); + } + + kldrDyldStackDropFrame(iLoad1st, iLoadEnd, rc); + return rc; +} + + +/** + * kldrDyldDoLoad() helper which will load prerequisites and + * build the initialization array / list. + * + * @returns 0 on success, non-zero error code on failure. + * @param pMod The module to start at. + * @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. + */ +static int kldrDyldDoLoadPrerequisites(PKLDRDYLDMOD pMod, const char *pszPrefix, const char *pszSuffix, + KLDRDYLDSEARCH enmSearch, unsigned fFlags) +{ + static struct + { + /** The module. */ + PKLDRDYLDMOD pMod; + /** The number of prerequisite modules left to process. + * This starts at ~0U to inidicate that we need to load/check prerequisistes. */ + unsigned cLeft; + } s_aEntries[64]; + unsigned cEntries; + int rc = 0; + + /* Prerequisites are always global and they just aren't executables. */ + fFlags &= ~(KLDRYDLD_LOAD_FLAGS_SPECIFIC_MODULE | KLDRDYLD_LOAD_FLAGS_EXECUTABLE); + + /* push the first entry. */ + s_aEntries[0].pMod = pMod; + s_aEntries[0].cLeft = ~0U; + cEntries = 1; + + /* + * The recursion loop. + */ + while (!rc && cEntries > 0) + { + const unsigned i = cEntries - 1; + pMod = s_aEntries[i].pMod; + if (s_aEntries[i].cLeft == ~0U) + { + /* + * Load prerequisite modules. + */ + switch (pMod->enmState) + { + /* + * Load immediate prerequisite modules and push the ones needing + * attention onto the stack. + */ + case KLDRSTATE_MAPPED: + case KLDRSTATE_RELOADED: + case KLDRSTATE_PENDING_TERMINATION: + rc = kldrDyldModLoadPrerequisites(pMod, pszPrefix, pszSuffix, enmSearch, fFlags); + KLDRDYLD_ASSERT( pMod->enmState == KLDRSTATE_GOOD + || pMod->enmState == KLDRSTATE_RELOADED_LOADED_PREREQUISITES + || pMod->enmState == KLDRSTATE_LOADED_PREREQUISITES + || rc); + if (!rc) + s_aEntries[i].cLeft = pMod->cPrereqs; + break; + + /* + * Check its prerequisite modules the first time around. + */ + case KLDRSTATE_PENDING_INITIALIZATION: + if (pMod->fAlreadySeen) + break; + pMod->fAlreadySeen = 1; + s_aEntries[i].cLeft = pMod->cPrereqs; + break; + + /* + * These are ok. + */ + case KLDRSTATE_LOADED_PREREQUISITES: + case KLDRSTATE_RELOADED_LOADED_PREREQUISITES: + case KLDRSTATE_INITIALIZING: + case KLDRSTATE_GOOD: + s_aEntries[i].cLeft = 0; + break; + + /* + * All other stats are invalid. + */ + default: + KLDRDYLD_ASSERT(!"invalid state"); + break; + } + } + else if (s_aEntries[i].cLeft > 0) + { + /* + * Recurse down into the next prereq. + */ + KLDRDYLD_ASSERT(s_aEntries[i].cLeft <= pMod->cPrereqs); + if (cEntries < sizeof(s_aEntries) / sizeof(s_aEntries[0])) + { + s_aEntries[cEntries].cLeft = ~(KU32)0; + s_aEntries[cEntries].pMod = pMod->papPrereqs[pMod->cPrereqs - s_aEntries[i].cLeft]; + s_aEntries[i].cLeft--; + cEntries++; + } + else + rc = KLDR_ERR_PREREQUISITE_RECURSED_TOO_DEEPLY; + } + else + { + /* + * We're done with this module, record it for init/cleanup. + */ + cEntries--; + if (pMod->enmState != KLDRSTATE_GOOD) + { + kldrDyldStackAddModule(pMod); + if ( !(fFlags & KLDRDYLD_LOAD_FLAGS_RECURSIVE_INIT) + && !pMod->fInitList) + { + pMod->fInitList = 1; + pMod->InitTerm.pNext = NULL; + pMod->InitTerm.pPrev = g_pkLdrDyldInitTail; + if (g_pkLdrDyldInitTail) + g_pkLdrDyldInitTail->InitTerm.pNext = pMod; + else + g_pkLdrDyldInitHead = pMod; + g_pkLdrDyldInitTail = pMod; + } + } + } + } + + 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 pszPrefix Prefix than can be used when searching. + * @param pszSuffix 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 *pszPrefix, const char *pszSuffix, KLDRDYLDSEARCH enmSearch, + unsigned fFlags, PKLDRDYLDMOD pDep, PPKLDRDYLDMOD ppMod) +{ + int rc; + PKLDRDYLDMOD pMod; + + *ppMod = NULL; + + /* + * Try find the module among the ones that's already loaded. + * + * This is very similar to the kldrDyldDoLoad code, except it has to deal with + * a couple of additional states and occurs only during prerequisite loading + * and the action taken is a little bit different. + */ + rc = kldrDyldFindExistingModule(pszDll, pszPrefix, pszSuffix, enmSearch, fFlags, &pMod); + if (!rc) + { + switch (pMod->enmState) + { + /* + * These are good. + */ + 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: + 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(pMod); + break; + + /* + * The module can't be loaded because it failed to initialize already. + */ + case KLDRSTATE_INITIALIZATION_FAILED: + rc = KLDR_ERR_PREREQUISITE_MODULE_INIT_FAILED; + break; + + /* + * Forget it, no idea how to deal with re-initialization. + */ + case KLDRSTATE_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, pszPrefix, pszSuffix, enmSearch, fFlags, &pMod); + if (!rc) + rc = kldrDyldModMap(pMod); + } + + /* + * On success add dependency. + */ + if (!rc) + { + kldrDyldModAddDep(pMod, pDep); + *ppMod = pMod; + } + return rc; +} + + +/** + * Starts a new load stack frame. + * + * @returns Where the new stack frame starts. + * @param pLoadMod The module being loaded (only used for asserting). + */ +static KU32 kldrDyldStackNewFrame(PKLDRDYLDMOD pLoadMod) +{ + /* + * Clear the fAlreadySeen flags. + */ + PKLDRDYLDMOD pMod = kLdrDyldHead; + while (pMod) + { + pMod->fAlreadySeen = 0; + +#ifdef KLDRDYLD_ASSERT + switch (pMod->enmState) + { + case KLDRSTATE_MAPPED: + case KLDRSTATE_RELOADED: + /* only the just loaded module can be in this state. */ + KLDRDYLD_ASSERT(pMod == pLoadMod); + break; + + case KLDRSTATE_PENDING_INITIALIZATION: + case KLDRSTATE_INITIALIZING: + case KLDRSTATE_PENDING_TERMINATION: + case KLDRSTATE_PENDING_GC: + case KLDRSTATE_TERMINATING: + case KLDRSTATE_INITIALIZATION_FAILED: + case KLDRSTATE_PENDING_DESTROY: + /* requires recursion. */ + KLDRDYLD_ASSERT(g_cActiveLoadCalls + g_cActiveLoadCalls + g_fActiveGC > 1); + break; + + case KLDRSTATE_GOOD: + /* requires nothing. */ + break; + + default: + KLDRDYLD_ASSERT(!"Invalid state"); + break; + } +#endif + + /* next */ + pMod = pMod->Load.pNext; + } + return g_cStackMods; +} + + +/** + * Records the module. + * + * @return 0 on success, KERR_NO_MEMORY if we can't expand the table. + * @param pMod The module to record. + */ +static int kldrDyldStackAddModule(PKLDRDYLDMOD pMod) +{ + /* + * Grow the stack if necessary. + */ + if (g_cStackMods + 1 > g_cStackModsAllocated) + { + KU32 cNew = g_cStackModsAllocated ? g_cStackModsAllocated * 2 : 128; + void *pvOld = g_papStackMods; + void *pvNew = kHlpAlloc(cNew * sizeof(g_papStackMods[0])); + if (!pvNew) + return KERR_NO_MEMORY; + kHlpMemCopy(pvNew, pvOld, g_cStackMods * sizeof(g_papStackMods[0])); + g_papStackMods = (PPKLDRDYLDMOD)pvNew; + kHlpFree(pvOld); + } + + /* + * Add a reference and push the module onto the stack. + */ + kldrDyldModAddRef(pMod); + g_papStackMods[g_cStackMods++] = pMod; + return 0; +} + + +/** + * The frame has been completed. + * + * @returns Where the frame ends. + */ +static int kldrDyldStackFrameCompleted(void) +{ + return g_cStackMods; +} + + +/** + * Worker routine for kldrDyldStackDropFrame() and kldrDyldDoLoad(). + * + * @param pMod The module to perform cleanups on. + * @param rc Used for state verification. + */ +static void kldrDyldStackCleanupOne(PKLDRDYLDMOD pMod, int rc) +{ + switch (pMod->enmState) + { + /* + * Just push it along to the PENDING_DESTROY state. + */ + case KLDRSTATE_MAPPED: + KLDRDYLD_ASSERT(rc); + kldrDyldModUnmap(pMod); + KLDRDYLD_ASSERT(pMod->enmState == KLDRSTATE_PENDING_DESTROY); + break; + + /* + * Move back to PENDING_GC. + */ + case KLDRSTATE_RELOADED: + KLDRDYLD_ASSERT(rc); + pMod->enmState = KLDRSTATE_PENDING_GC; + break; + + /* + * Unload prerequisites and unmap the modules. + */ + case KLDRSTATE_LOADED_PREREQUISITES: + case KLDRSTATE_FIXED_UP: + KLDRDYLD_ASSERT(rc); + kldrDyldModUnloadPrerequisites(pMod); + KLDRDYLD_ASSERT(pMod->enmState == KLDRSTATE_PENDING_DESTROY); + kldrDyldModUnmap(pMod); + KLDRDYLD_ASSERT(pMod->enmState == KLDRSTATE_PENDING_DESTROY); + break; + + /* + * Unload prerequisites and push it back to PENDING_GC. + */ + case KLDRSTATE_RELOADED_LOADED_PREREQUISITES: + case KLDRSTATE_RELOADED_FIXED_UP: + kldrDyldModUnloadPrerequisites(pMod); + KLDRDYLD_ASSERT(pMod->enmState == KLDRSTATE_PENDING_GC); + break; + + /* + * Nothing to do, just asserting sanity. + */ + case KLDRSTATE_INITIALIZING: + /* Implies there is another load going on. */ + KLDRDYLD_ASSERT(g_cActiveLoadCalls > 1); + break; + case KLDRSTATE_TERMINATING: + /* GC in progress. */ + 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; + } +} + + +/** + * Done with the stack frame, dereference all the modules in it. + * + * @param iLoad1st The start of the stack frame. + * @param iLoadEnd The end of the stack frame. + * @param rc Used for state verification. + */ +static void kldrDyldStackDropFrame(KU32 iLoad1st, KU32 iLoadEnd, int rc) +{ + KU32 i; + KLDRDYLD_ASSERT(iLoad1st <= g_cStackMods); + KLDRDYLD_ASSERT(iLoadEnd == g_cStackMods); + + /* + * First pass: Do all the cleanups we can, but don't destroy anything just yet. + */ + i = iLoadEnd; + while (i-- > iLoad1st) + { + PKLDRDYLDMOD pMod = g_papStackMods[i]; + kldrDyldStackCleanupOne(pMod, rc); + } + + /* + * Second pass: Release the references so modules pending destruction + * can be completely removed. + */ + for (i = iLoad1st; i < iLoadEnd ; i++) + { + PKLDRDYLDMOD pMod = g_papStackMods[i]; + + /* + * Revalidate the module state. + */ + switch (pMod->enmState) + { + case KLDRSTATE_INITIALIZING: + case KLDRSTATE_TERMINATING: + case KLDRSTATE_PENDING_TERMINATION: + case KLDRSTATE_PENDING_INITIALIZATION: + case KLDRSTATE_PENDING_GC: + case KLDRSTATE_PENDING_DESTROY: + case KLDRSTATE_GOOD: + break; + default: + KLDRDYLD_ASSERT(!"drop frame bad state (b)"); + break; + } + + /* + * Release it. + */ + kldrDyldModDeref(pMod); + } + + /* + * Drop the stack frame. + */ + g_cStackMods = iLoad1st; +} + + +/** + * 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 until we're got rid of all recursive 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; + + do + { + /* + * 1. Release prerequisites for any left over modules. + */ + for (pMod = kLdrDyldHead; pMod; pMod = pMod->Load.pNext) + { + kldrDyldModAddRef(pMod); + + switch (pMod->enmState) + { + case KLDRSTATE_GOOD: + case KLDRSTATE_PENDING_GC: + case KLDRSTATE_PENDING_TERMINATION: + break; + + case KLDRSTATE_INITIALIZATION_FAILED: /* just in case */ + case KLDRSTATE_PENDING_INITIALIZATION: + kldrDyldModUnloadPrerequisites(pMod); + break; + + default: + KLDRDYLD_ASSERT(!"invalid GC state (a)"); + break; + } + + kldrDyldModDeref(pMod); + } + + /* + * 2. Do init calls until we encounter somebody calling load/unload. + */ + for (pMod = g_pkLdrDyldTermHead; pMod; pMod = pMod->InitTerm.pNext) + { + int fRestart = 0; + kldrDyldModAddRef(pMod); + + switch (pMod->enmState) + { + case KLDRSTATE_GOOD: + case KLDRSTATE_PENDING_GC: + break; + + case KLDRSTATE_PENDING_TERMINATION: + { + const KU32 cTotalLoadCalls = g_cTotalLoadCalls; + const KU32 cTotalUnloadCalls = g_cTotalUnloadCalls; + kldrDyldModCallTerm(pMod); + fRestart = cTotalLoadCalls != g_cTotalLoadCalls + || cTotalUnloadCalls != g_cTotalUnloadCalls; + 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; + kldrDyldModAddRef(pMod); + + switch (pMod->enmState) + { + case KLDRSTATE_INITIALIZATION_FAILED: + case KLDRSTATE_PENDING_GC: + KLDRDYLD_ASSERT(!pMod->cDepRefs); + KLDRDYLD_ASSERT(!pMod->cDynRefs); + pMod->enmState = KLDRSTATE_GC; + kldrDyldModUnmap(pMod); + KLDRDYLD_ASSERT(pMod->enmState == KLDRSTATE_PENDING_DESTROY); + kldrDyldModDestroy(pMod); + KLDRDYLD_ASSERT(pMod->enmState == KLDRSTATE_DESTROYED); + break; + + case KLDRSTATE_GOOD: + break; + default: + KLDRDYLD_ASSERT(!"invalid GC state (c)"); + break; + } + + kldrDyldModDeref(pMod); + + /* next */ + pMod = pNext; + } + + g_fActiveGC = 0; +} + + +/** + * Worker for kLdrDyldUnload(). + * @internal + */ +static int kldrDyldDoUnload(PKLDRDYLDMOD pMod) +{ + return kldrDyldModDynamicUnload(pMod); +} + + +/** + * Worker for kLdrDyldFindByName(). + * @internal + */ +static int kldrDyldDoFindByName(const char *pszDll, const char *pszPrefix, const char *pszSuffix, KLDRDYLDSEARCH enmSearch, + unsigned fFlags, PPKLDRDYLDMOD ppMod) +{ + return kldrDyldFindExistingModule(pszDll, pszPrefix, pszSuffix, enmSearch, fFlags, ppMod); +} + + +/** + * Worker for kLdrDyldFindByAddress(). + * @internal + */ +static int kldrDyldDoFindByAddress(KUPTR Address, PPKLDRDYLDMOD ppMod, KU32 *piSegment, KUPTR *poffSegment) +{ + /* Scan the segments of each module in the load list. */ + PKLDRDYLDMOD pMod = kLdrDyldHead; + while (pMod) + { + KU32 iSeg; + for (iSeg = 0; iSeg < pMod->pMod->cSegments; iSeg++) + { + KLDRADDR off = (KLDRADDR)Address - pMod->pMod->aSegments[iSeg].MapAddress; + if (off < pMod->pMod->aSegments[iSeg].cb) + { + *ppMod = pMod->hMod; + if (piSegment) + *piSegment = iSeg; + if (poffSegment) + *poffSegment = (KUPTR)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, KSIZE cchName) +{ + return kldrDyldModGetName(pMod, pszName, cchName); +} + + +/** + * Worker for kLdrDyldGetFilename(). + * @internal + */ +static int kldrDyldDoGetFilename(PKLDRDYLDMOD pMod, char *pszFilename, KSIZE cchFilename) +{ + return kldrDyldModGetFilename(pMod, pszFilename, cchFilename); +} + + +/** + * Worker for kLdrDyldQuerySymbol(). + * @internal + */ +static int kldrDyldDoQuerySymbol(PKLDRDYLDMOD pMod, KU32 uSymbolOrdinal, const char *pszSymbolName, KUPTR *pValue, KU32 *pfKind) +{ + return kldrDyldModQuerySymbol(pMod, uSymbolOrdinal, pszSymbolName, pValue, pfKind); +} + + +/** + * 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 kldrDyldFailure(int rc, const char *pszFilename, ...) +{ + /** @todo print it. */ + if (g_fBootstrapping); + kHlpExit(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, KSIZE cchErr) +{ + KSIZE cchToCopy; + + /* if no error string, format the rc into a string. */ + if (!g_szkLdrDyldError[0] && rc) + kHlpInt2Ascii(g_szkLdrDyldError, sizeof(g_szkLdrDyldError), rc, 10); + + /* copy it if we got something. */ + if (cchErr && pszErr && g_szkLdrDyldError[0]) + { + cchToCopy = kHlpStrLen(g_szkLdrDyldError); + if (cchToCopy >= cchErr) + cchToCopy = cchErr - 1; + kHlpMemCopy(pszErr, g_szkLdrDyldError, cchToCopy); + pszErr[cchToCopy] = '\0'; + } + + return rc; +} + |