/* $Id: kLdrDy.c 2833 2006-10-26 00:08:09Z bird $ */ /** @file * * kLdr - The Dynamic Loader. * * Copyright (c) 2006 knut st. osmundsen * * * This file is part of kLdr. * * kLdr is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * kLdr is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with kLdr; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ /******************************************************************************* * Header Files * *******************************************************************************/ #include #include "kLdrHlp.h" #include "kLdrInternal.h" /******************************************************************************* * Global Variables * *******************************************************************************/ /** Pointer to the head module (the executable). * (This is exported, so no prefix.) */ PKLDRDYLDMOD kLdrDyldHead = NULL; /** Pointer to the tail module. * (This is exported, so no prefix.) */ PKLDRDYLDMOD kLdrDyldTail = NULL; /** The Library search path. */ char kLdrDyldLibraryPath[4096]; /** The executable flags. */ uint32_t kLdrDyldFlags; /******************************************************************************* * Internal Functions * *******************************************************************************/ static int kldrDyldLoad(const char *pszDll, const char *pszDefPrefix, const char *pszDefSuffix, KLDRDYLDSEARCH enmSearch, unsigned fFlags, PPKLDRDYLDMOD ppMod, char *pszErr, size_t cchErr); static int kldrDyldUnload(PKLDRDYLDMOD pMod); static int kldrDyldFindByName(const char *pszDll, const char *pszDefPrefix, const char *pszDefSuffix, KLDRDYLDSEARCH enmSearch, PPKLDRDYLDMOD ppMod); static int kldrDyldFindByAddress(uintptr_t Address, PPKLDRDYLDMOD ppMod, uint32_t *piSegment, uintptr_t *poffSegment); static int kldrDyldGetName(PKLDRDYLDMOD pMod, char *pszName, size_t cchName); static int kldrDyldGetFilename(PKLDRDYLDMOD pMod, char *pszFilename, size_t cchFilename); static int kldrDyldQuerySymbol(PKLDRDYLDMOD pMod, uint32_t uSymbolOrdinal, const char *pszSymbolName, uintptr_t *pValue, uint32_t *pfKind); /** * Initialize the dynamic loader. */ int kldrDyInit(void) { kLdrDyldHead = NULL; kLdrDyldTail = NULL; kLdrDyldFlags = 0; return 0; } /** * Terminate the dynamic loader. */ void kldrDyTerm(void) { } /** * Loads a module into the current process. * * @returns 0 on success, non-zero native OS status code or kLdr status code on failure. * @param pszDll The name of the dll to open. * @param pszDefPrefix Prefix to use when searching. * @param pszDefSuffix Suffix to use when searching. * @param enmSearch Method to use when locating the module and any modules it may depend on. * @param fFlags Flags, a combintation of the KLDRYDLD_LOAD_FLAGS_* \#defines. * @param phMod Where to store the handle to the loaded module. * @param pszErr Where to store extended error information. (optional) * @param cchErr The size of the buffer pointed to by pszErr. */ int kLdrDyldLoad(const char *pszDll, const char *pszDefPrefix, const char *pszDefSuffix, KLDRDYLDSEARCH enmSearch, unsigned fFlags, PHKLDRMOD phMod, char *pszErr, size_t cchErr) { int rc; /* validate arguments and initialize return values. */ if (pszErr && cchErr) *pszErr = '\0'; *phMod = NIL_HKLDRMOD; KLDRHLP_VALIDATE_STRING(pszDll); KLDRHLP_VALIDATE_OPTIONAL_STRING(pszDefPrefix); KLDRHLP_VALIDATE_OPTIONAL_STRING(pszDefSuffix); KLDRHLP_VALIDATE_ENUM(enmSearch, KLDRDYLD_SEARCH); KLDRHLP_VALIDATE_OPTIONAL_BUFFER(pszErr, cchErr); /* get the semaphore and do the job. */ rc = kldrHlpSemRequest(); if (!rc) { PKLDRDYLDMOD pMod = NULL; rc = kldrDyldLoad(pszDll, pszDefPrefix, pszDefSuffix, enmSearch, fFlags, phMod, pszErr, cchErr); kldrHlpSemRelease(); *phMod = pMod; } return rc; } /** * Unloads a module loaded by kLdrDyldLoad. * * @returns 0 on success, non-zero native OS status code or kLdr status code on failure. * @param hMod Module handle. */ int kLdrDyldUnload(HKLDRMOD hMod) { int rc; /* validate */ KLDRDYLD_VALIDATE_HKLDRMOD(hMod); /* get sem & do work */ rc = kldrHlpSemRequest(); if (!rc) { rc = kldrDyldUnload(hMod); kldrHlpSemRelease(); } return rc; } /** * Finds a module by name or filename. * * This call does not increase any reference counters and must not be * paired with kLdrDyldUnload() like kLdrDyldLoad(). * * @returns 0 on success. * @returns KLDR_ERR_MODULE_NOT_FOUND on failure. * @param pszDll The name of the dll to look for. * @param pszDefPrefix Prefix to use when searching. * @param pszDefSuffix Suffix to use when searching. * @param enmSearch Method to use when locating the module. * @param phMod Where to store the handle of the module on success. */ int kLdrDyldFindByName(const char *pszDll, const char *pszDefPrefix, const char *pszDefSuffix, KLDRDYLDSEARCH enmSearch, PHKLDRMOD phMod) { int rc; /* validate & initialize */ *phMod = NIL_HKLDRMOD; KLDRHLP_VALIDATE_STRING(pszDll); /* get sem & do work */ rc = kldrHlpSemRequest(); if (!rc) { PKLDRDYLDMOD pMod = NULL; rc = kldrDyldFindByName(pszDll, pszDefPrefix, pszDefSuffix, enmSearch, phMod); kldrHlpSemRelease(); *phMod = pMod; } return rc; } /** * Finds a module by address. * * This call does not increase any reference counters and must not be * paired with kLdrDyldUnload() like kLdrDyldLoad(). * * @returns 0 on success. * @returns KLDR_ERR_MODULE_NOT_FOUND on failure. * @param Address The address believed to be within some module. * @param phMod Where to store the module handle on success. * @param piSegment Where to store the segment number. (optional) * @param poffSegment Where to store the offset into the segment. (optional) */ int kLdrDyldFindByAddress(uintptr_t Address, PHKLDRMOD phMod, uint32_t *piSegment, uintptr_t *poffSegment) { int rc; /* validate & initialize */ *phMod = NIL_HKLDRMOD; if (piSegment) *piSegment = ~(uint32_t)0; if (poffSegment) *poffSegment = ~(uintptr_t)0; /* get sem & do work */ rc = kldrHlpSemRequest(); if (!rc) { PKLDRDYLDMOD pMod = NULL; rc = kldrDyldFindByAddress(Address, &pMod, piSegment, poffSegment); kldrHlpSemRelease(); *phMod = pMod; } return rc; } /** * Gets the module name. * * @returns 0 on success and pszName filled with the name. * @returns KLDR_ERR_INVALID_HANDLE or KLDR_ERR_BUFFER_OVERFLOW on failure. * @param hMod The module handle. * @param pszName Where to put the name. * @param cchName The size of the name buffer. * @see kLdrDyldGetFilename */ int kLdrDyldGetName(HKLDRMOD hMod, char *pszName, size_t cchName) { int rc; /* validate */ if (pszName && cchName) *pszName = '\0'; KLDRDYLD_VALIDATE_HKLDRMOD(hMod); KLDRHLP_VALIDATE_BUFFER(pszName, cchName); /* get sem & do work */ rc = kldrHlpSemRequest(); if (!rc) { rc = kldrDyldGetName(hMod, pszName, cchName); kldrHlpSemRelease(); } return rc; } /** * Gets the module filename. * * @returns 0 on success and pszFilename filled with the name. * @returns KLDR_ERR_INVALID_HANDLE or KLDR_ERR_BUFFER_OVERFLOW on failure. * @param hMod The module handle. * @param pszFilename Where to put the filename. * @param cchFilename The size of the filename buffer. * @see kLdrDyldGetName */ int kLdrDyldGetFilename(HKLDRMOD hMod, char *pszFilename, size_t cchFilename) { int rc; /* validate & initialize */ if (pszFilename && cchFilename); *pszFilename = '\0'; KLDRDYLD_VALIDATE_HKLDRMOD(hMod); KLDRHLP_VALIDATE_BUFFER(pszFilename, cchFilename); /* get sem & do work */ rc = kldrHlpSemRequest(); if (!rc) { rc = kldrDyldGetFilename(hMod, pszFilename, cchFilename); kldrHlpSemRelease(); } return rc; } /** * Queries the value and type of a symbol. * * @returns 0 on success and pValue and pfKind set. * @returns KLDR_ERR_INVALID_HANDLE or KLDR_ERR_SYMBOL_NOT_FOUND on failure. * @param hMod The module handle. * @param uSymbolOrdinal The symbol ordinal. This is ignored if pszSymbolName is non-zero. * @param pszSymbolName The symbol name. * @param pValue Where to put the symbol value. Optional if pfKind is non-zero. * @param pfKind Where to put the symbol kind flags. Optional if pValue is non-zero. */ int kLdrDyldQuerySymbol(HKLDRMOD hMod, uint32_t uSymbolOrdinal, const char *pszSymbolName, uintptr_t *pValue, uint32_t *pfKind) { int rc; /* validate & initialize */ if (pfKind) *pfKind = 0; if (pValue) *pValue = 0; if (!pfKind && !pValue) return KLDR_ERR_INVALID_PARAMETER; KLDRDYLD_VALIDATE_HKLDRMOD(hMod); KLDRHLP_VALIDATE_OPTIONAL_STRING(pszSymbolName); /* get sem & do work */ rc = kldrHlpSemRequest(); if (!rc) { rc = kldrDyldQuerySymbol(hMod, uSymbolOrdinal, pszSymbolName, pValue, pfKind); kldrHlpSemRelease(); } return rc; } /** * Worker for kldrDyldLoad(). * @internal */ static int kldrDyldLoad(const char *pszDll, const char *pszDefPrefix, const char *pszDefSuffix, KLDRDYLDSEARCH enmSearch, unsigned fFlags, PPKLDRDYLDMOD ppMod, char *pszErr, size_t cchErr) { /* * Open the module. */ return -1; } /** * Worker for kldrDyldUnload(). * @internal */ static int kldrDyldUnload(HKLDRMOD hMod) { return -1; } /** * Worker for kLdrDyldFindByName(). * @internal */ static int kldrDyldFindByName(const char *pszDll, const char *pszDefPrefix, const char *pszDefSuffix, KLDRDYLDSEARCH enmSearch, PPKLDRDYLDMOD ppMod) { return -1; } /** * Worker for kLdrDyldFindByAddress(). * @internal */ static int kldrDyldFindByAddress(uintptr_t Address, PPKLDRDYLDMOD ppMod, uint32_t *piSegment, uintptr_t *poffSegment) { return -1; } /** * Worker for kLdrDyldGetName(). * @internal */ static int kldrDyldGetName(PKLDRDYLDMOD pMod, char *pszName, size_t cchName) { return -1; } /** * Worker for kLdrDyldGetFilename(). * @internal */ static int kldrDyldGetFilename(PKLDRDYLDMOD pMod, char *pszFilename, size_t cchFilename) { return -1; } /** * Worker for kLdrDyldQuerySymbol(). * @internal */ static int kldrDyldQuerySymbol(PKLDRDYLDMOD pMod, uint32_t uSymbolOrdinal, const char *pszSymbolName, uintptr_t *pValue, uint32_t *pfKind) { return -1; } #if 0 void kldrLoadExe(PKLDREXEARGS pArgs) { /* * Copy the arguments into the globals and do load init. */ kLdrFlags = pArgs->fFlags; kLdrHlpMemCopy(kLdrLibraryPath, pArgs->szLibPath, KLDR_MIN(sizeof(pArgs->szLibPath), sizeof(kLdrLibraryPath))); int rc = kldrInit(); if (rc) kldrFailure(rc, "kLdr: Init failure, rc=%d\n", rc); /* * Open the executable module. */ PKLDRMOD pExe; kldrOpenExe(pArgs->szExecutable, &pExe); /* Map the segments. */ kldrModMapSegments(pExe); /* * This is the point where we switch to the executable * stack, allocating it if necessary. */ void *pvBottom; kldrModSetupStack(pExe, &pvBottom); kldrLoadExecSwitchStack(pvBottom); } void kldrLoadExeOnNewStack(void) { /* * Load all dependant modules. */ PKLDRMOD pCur; do for (pCur = kLdrModuleHead; pCur; pCur = pCur->pNext) { if (pCur->enmState >= KLDRSTATE_DEPS) continue; kldrModLoadDeps(pCur); } while (pCur); /* * Do fixups (FIFO). */ for (pCur = kLdrModuleHead; pCur; pCur = pCur->pNext) { if (pCur->enmState >= KLDRSTATE_FIXED) continue; kldrModFixup(pCur, 0); } /* * Do module initialization. */ for (pCur = kLdrModuleTail; pCur != kLdrModuleTail; pCur = pCur->pPrev) { if (pCur->enmState >= KLDRSTATE_INITED) continue; kldrModCallInit(pCur); } /* * Get the executable start address and commit the work that's been done. */ void *pvEntry; kldrModGetExeEntry(&pvEntry); for (pCur = kLdrModuleHead; pCur; pCur = pCur->pNext) if (pCur->enmState == KLDRSTATE_INITED) pCur->enmState = KLDRSTATE_LOADED; kldrHlpSemRelease(); /* * We're now ready for starting the executable code. */ kldrOSStartExe(pLdrModuleHead, pvEntry); } /** * Panic / failure * * @returns rc if we're in a position where we can return. * @param rc Return code. * @param pszFormat Message string. Limited fprintf like formatted. * @param ... Message string arguments. */ int kldrFailure(int rc, const char *pszFormat, ...) { kldrExit(1); return rc; } #endif