/* $Id: kLdrDyldFind.c 2886 2006-11-18 15:01:05Z bird $ */ /** @file * * kLdr - The Dynamic Loader, File Searching 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 * *******************************************************************************/ #ifdef __OS2__ # define INCL_BASE # define INCL_ERRORS # include # ifndef LIBPATHSTRICT # define LIBPATHSTRICT 3 # endif extern APIRET APIENTRY DosQueryHeaderInfo(HMODULE hmod, ULONG ulIndex, PVOID pvBuffer, ULONG cbBuffer, ULONG ulSubFunction); # define QHINF_EXEINFO 1 /* NE exeinfo. */ # define QHINF_READRSRCTBL 2 /* Reads from the resource table. */ # define QHINF_READFILE 3 /* Reads from the executable file. */ # define QHINF_LIBPATHLENGTH 4 /* Gets the libpath length. */ # define QHINF_LIBPATH 5 /* Gets the entire libpath. */ # define QHINF_FIXENTRY 6 /* NE only */ # define QHINF_STE 7 /* NE only */ # define QHINF_MAPSEL 8 /* NE only */ #elif defined(__WIN__) # include #endif #include #include "kLdrHlp.h" #include "kLdrInternal.h" /******************************************************************************* * Defined Constants And Macros * *******************************************************************************/ /** @def KLDRDYLDFIND_STRICT * Define KLDRDYLDFIND_STRICT to enabled strict checks in kLdrDyldFind. */ #define KLDRDYLDFIND_STRICT 1 /** @def KLDRDYLDFIND_ASSERT * Assert that an expression is true when KLDRDYLDFIND_STRICT is defined. */ #ifdef KLDRDYLDFIND_STRICT # define KLDRDYLDFIND_ASSERT(expr) kldrHlpAssert(expr) #else # define KLDRDYLDFIND_ASSERT(expr) do {} while (0) #endif /******************************************************************************* * Structures and Typedefs * *******************************************************************************/ /** * Search arguments. * This avoids a bunch of unnecessary string lengths and calculations. */ typedef struct KLDRDYLDFINDARGS { const char *pszName; size_t cchName; const char *pszPrefix; size_t cchPrefix; const char *pszSuffix; size_t cchSuffix; size_t cchMaxLength; KLDRDYLDSEARCH enmSearch; uint32_t fFlags; PPKLDRRDR ppRdr; } KLDRDYLDFINDARGS, *PKLDRDYLDFINDARGS; typedef const KLDRDYLDFINDARGS *PCKLDRDYLDFINDARGS; /******************************************************************************* * Global Variables * *******************************************************************************/ /** @name The kLdr search method parameters. * @{ */ /** The kLdr EXE search path. * During EXE searching the it's initialized with the values of the KLDR_PATH and * the PATH env.vars. Both ';' and ':' can be used as separators. */ char kLdrDyldExePath[8192]; /** The kLdr DLL search path. * During initialization the KLDR_LIBRARY_PATH env.var. and the path in the * executable stub is appended. Both ';' and ':' can be used as separators. */ char kLdrDyldLibraryPath[8192]; /** The kLdr application directory. * This is initialized when the executable is 'loaded' or by a kLdr user. */ char kLdrDyldAppDir[260]; /** The default kLdr DLL prefix. * This is initialized with the KLDR_DEF_PREFIX env.var. + the prefix in the executable stub. */ char kLdrDyldDefPrefix[16]; /** The default kLdr DLL suffix. * This is initialized with the KLDR_DEF_SUFFIX env.var. + the prefix in the executable stub. */ char kLdrDyldDefSuffix[16]; /** @} */ /** @name The OS/2 search method parameters. * @{ */ /** The OS/2 LIBPATH. * This is queried from the os2krnl on OS/2, while on other systems initialized using * the KLDR_OS2_LIBPATH env.var. */ char kLdrDyldOS2Libpath[2048]; /** The OS/2 LIBPATHSTRICT ("T" or '\0'). * This is queried from the os2krnl on OS/2, while on other systems initialized using * the KLDR_OS2_LIBPATHSTRICT env.var. */ char kLdrDyldOS2LibpathStrict[8]; /** The OS/2 BEGINLIBPATH. * This is queried from the os2krnl on OS/2, while on other systems initialized using * the KLDR_OS2_BEGINLIBPATH env.var. */ char kLdrDyldOS2BeginLibpath[2048]; /** The OS/2 ENDLIBPATH. * This is queried from the os2krnl on OS/2, while on other systems initialized using * the KLDR_OS2_ENDLIBPATH env.var. */ char kLdrDyldOS2EndLibpath[2048]; /** @} */ /** @name The Windows search method parameters. * @{ */ /** The Windows application directory. * This is initialized when the executable is 'loaded' or by a kLdr user. */ char kLdrDyldWindowsAppDir[260]; /** The Windows system directory. * This is queried from the Win32/64 subsystem on Windows, while on other systems * initialized using the KLDR_WINDOWS_SYSTEM_DIR env.var. */ char kLdrDyldWindowsSystemDir[260]; /** The Windows directory. * This is queried from the Win32/64 subsystem on Windows, while on other systems * initialized using the KLDR_WINDOWS_DIR env.var. */ char kLdrDyldWindowsDir[260]; /** The Windows path. * This is queried from the PATH env.var. on Windows, while on other systems * initialized using the KLDR_WINDOWS_PATH env.var. and falling back on * the PATH env.var. if it wasn't found. */ char kLdrDyldWindowsPath[8192]; /** @} */ /** @name The Common Unix search method parameters. * @{ */ /** The Common Unix library path. * Initialized from the env.var. KLDR_UNIX_LIBRARY_PATH or LD_LIBRARY_PATH or the * former wasn't found. */ char kLdrDyldUnixLibraryPath[8192]; /** The Common Unix system library path. */ char kLdrDyldUnixSystemLibraryPath[1024] = "/lib;/usr/lib"; /** @} */ /** @todo Deal with DT_RUNPATH and DT_RPATH. */ /** @todo ld.so.cache? */ /******************************************************************************* * Internal Functions * *******************************************************************************/ static int kldrDyldFindDoDllSearch(const char *pszName, const char *pszPrefix, const char *pszSuffix, KLDRDYLDSEARCH enmSearch, unsigned fFlags, PPKLDRRDR ppRdr); static int kldrDyldFindDoExeSearch(const char *pszName, const char *pszPrefix, const char *pszSuffix, KLDRDYLDSEARCH enmSearch, unsigned fFlags, PPKLDRRDR ppRdr); static int kldrDyldFindTryOpen(const char *pszFilename, PPKLDRRDR ppRdr); static int kldrDyldFindTryOpenPath(const char *pchPath, size_t cchPath, PCKLDRDYLDFINDARGS pArgs); static int kldrDyldFindEnumeratePath(const char *pszSearchPath, PCKLDRDYLDFINDARGS pArgs); static int kldrDyldFindGetDefaults(KLDRDYLDSEARCH *penmSearch, const char **pszPrefix, const char **pszSuffix, const char *pszName, uint32_t fFlags); /** * Initializes the find paths. * * @returns 0 on success, non-zero on failure. */ int kldrDyldFindInit(void) { size_t cch; int rc; char szTmp[sizeof(kLdrDyldDefSuffix)]; /* * The kLdr search parameters. */ rc = kldrHlpGetEnv("KLDR_LIBRARY_PATH", kLdrDyldLibraryPath, sizeof(kLdrDyldLibraryPath)); rc = kldrHlpGetEnv("KLDR_DEF_PREFIX", szTmp, sizeof(szTmp)); if (!rc) kLdrHlpMemCopy(kLdrDyldDefPrefix, szTmp, sizeof(szTmp)); rc = kldrHlpGetEnv("KLDR_DEF_SUFFIX", szTmp, sizeof(szTmp)); if (!rc) kLdrHlpMemCopy(kLdrDyldDefSuffix, szTmp, sizeof(szTmp)); /* * The OS/2 search parameters. */ #ifdef __OS2__ rc = DosQueryHeaderInfo(NULLHANDLE, 0, kLdrDyldOS2Libpath, sizeof(kLdrDyldOS2Libpath), QHINF_LIBPATH); if (rc) return rc; rc = DosQueryExtLIBPATH((PSZ)kLdrDyldOS2LibpathStrict, LIBPATHSTRICT); if (rc) kLdrDyldOS2LibpathStrict[0] = '\0'; rc = DosQueryExtLIBPATH((PSZ)kLdrDyldOS2BeginLibpath, BEGIN_LIBPATH); if (rc) kLdrDyldOS2BeginLibpath[0] = '\0'; rc = DosQueryExtLIBPATH((PSZ)kLdrDyldOS2EndLibpath, END_LIBPATH); if (rc) kLdrDyldOS2EndLibpath[0] = '\0'; #else kldrHlpGetEnv("KLDR_OS2_LIBPATH", kLdrDyldOS2Libpath, sizeof(kLdrDyldOS2Libpath)); kldrHlpGetEnv("KLDR_OS2_LIBPATHSTRICT", kLdrDyldOS2LibpathStrict, sizeof(kLdrDyldOS2LibpathStrict)); if ( kLdrDyldOS2LibpathStrict[0] == 'T' || kLdrDyldOS2LibpathStrict[0] == 't') kLdrDyldOS2LibpathStrict[0] = 'T'; else kLdrDyldOS2LibpathStrict[0] = '\0'; kLdrDyldOS2LibpathStrict[1] = '\0'; kldrHlpGetEnv("KLDR_OS2_BEGINLIBPATH", kLdrDyldOS2BeginLibpath, sizeof(kLdrDyldOS2BeginLibpath)); kldrHlpGetEnv("KLDR_OS2_ENDLIBPATH", kLdrDyldOS2EndLibpath, sizeof(kLdrDyldOS2EndLibpath)); #endif /* * The windows search parameters. */ #if defined(__WIN__) cch = GetSystemDirectory(kLdrDyldWindowsSystemDir, sizeof(kLdrDyldWindowsSystemDir)); if (cch >= sizeof(kLdrDyldWindowsSystemDir)) return (rc = GetLastError()) ? rc : -1; cch = GetWindowsDirectory(kLdrDyldWindowsDir, sizeof(kLdrDyldWindowsDir)); if (cch >= sizeof(kLdrDyldWindowsDir)) return (rc = GetLastError()) ? rc : -1; kldrHlpGetEnv("PATH", kLdrDyldWindowsPath, sizeof(kLdrDyldWindowsPath)); #else kldrHlpGetEnv("KLDR_WINDOWS_SYSTEM_DIR", kLdrDyldWindowsSystemDir, sizeof(kLdrDyldWindowsSystemDir)); kldrHlpGetEnv("KLDR_WINDOWS_DIR", kLdrDyldWindowsDir, sizeof(kLdrDyldWindowsDir)); rc = kldrHlpGetEnv("KLDR_WINDOWS_PATH", kLdrDyldWindowsPath, sizeof(kLdrDyldWindowsPath)); if (rc) kldrHlpGetEnv("PATH", kLdrDyldWindowsPath, sizeof(kLdrDyldWindowsPath)); #endif /* * The Unix search parameters. */ rc = kldrHlpGetEnv("KLDR_UNIX_LIBRARY_PATH", kLdrDyldUnixLibraryPath, sizeof(kLdrDyldUnixLibraryPath)); if (rc) kldrHlpGetEnv("LD_LIBRARY_PATH", kLdrDyldUnixLibraryPath, sizeof(kLdrDyldUnixLibraryPath)); (void)cch; return 0; } /** * Lazily initialize the two application directory paths. */ static void kldrDyldFindLazyInitAppDir(void) { if (!kLdrDyldAppDir[0]) { #if defined(__OS2__) PPIB pPib; PTIB pTib; APIRET rc; DosGetInfoBlocks(&pTib, &pPib); rc = DosQueryModuleName(pPib->pib_hmte, sizeof(kLdrDyldAppDir), kLdrDyldAppDir); if (!rc) { *kldrHlpGetFilename(kLdrDyldAppDir) = '\0'; kLdrHlpMemCopy(kLdrDyldWindowsAppDir, kLdrDyldAppDir, sizeof(kLdrDyldAppDir)); } else { kLdrDyldWindowsAppDir[0] = kLdrDyldAppDir[0] = '.'; kLdrDyldWindowsAppDir[1] = kLdrDyldAppDir[1] = '\0'; } #elif defined(__WIN__) DWORD dwSize = GetModuleFileName(NULL /* the executable */, kLdrDyldAppDir, sizeof(kLdrDyldAppDir)); if (dwSize > 0) { *kldrHlpGetFilename(kLdrDyldAppDir) = '\0'; kLdrHlpMemCopy(kLdrDyldWindowsAppDir, kLdrDyldAppDir, sizeof(kLdrDyldAppDir)); } else { kLdrDyldWindowsAppDir[0] = kLdrDyldAppDir[0] = '.'; kLdrDyldWindowsAppDir[1] = kLdrDyldAppDir[1] = '\0'; } #else # error "Port me" #endif } } /** * Locates and opens a module using the specified search method. * * @returns 0 and *ppMod on success, non-zero OS specific error on failure. * * @param pszName Partial or complete name, it's specific to the search method to determin which. * @param pszPrefix Prefix than can be used when searching. * @param pszSuffix Suffix than can be used when searching. * @param enmSearch The file search method to apply. * @param fFlags Search flags. * @param ppMod Where to store the file provider instance on success. */ int kldrDyldFindNewModule(const char *pszName, const char *pszPrefix, const char *pszSuffix, KLDRDYLDSEARCH enmSearch, unsigned fFlags, PPKLDRDYLDMOD ppMod) { int rc; PKLDRRDR pRdr = NULL; *ppMod = NULL; /* * If this isn't just a filename, we the caller has specified a file * that should be opened directly and not a module name to be searched for. */ if (!kldrHlpIsFilenameOnly(pszName)) rc = kldrDyldFindTryOpen(pszName, &pRdr); else if (!(fFlags & KLDRDYLD_LOAD_FLAGS_EXECUTABLE)) rc = kldrDyldFindDoDllSearch(pszName, pszPrefix, pszSuffix, enmSearch, fFlags, &pRdr); else rc = kldrDyldFindDoExeSearch(pszName, pszPrefix, pszSuffix, enmSearch, fFlags, &pRdr); if (!rc) { #ifdef KLDRDYLDFIND_STRICT /* Sanity check of kldrDyldFindExistingModule. */ if (fFlags & KLDRYDLD_LOAD_FLAGS_SPECIFIC_MODULE) { const char *pszFilename = kLdrRdrName(pRdr); const size_t cchFilename = kLdrHlpStrLen(pszFilename); PKLDRDYLDMOD pCur; for (pCur = kLdrDyldHead; pCur; pCur = pCur->Load.pNext) KLDRDYLDFIND_ASSERT( pCur->pMod->cchFilename != cchFilename || kLdrHlpMemComp(pCur->pMod->pszFilename, pszFilename, cchFilename)); } #endif /* * Check for matching non-global modules that should be promoted. */ if (!(fFlags & KLDRYDLD_LOAD_FLAGS_SPECIFIC_MODULE)) { const char *pszFilename = kLdrRdrName(pRdr); const size_t cchFilename = kLdrHlpStrLen(pszFilename); PKLDRDYLDMOD pCur; for (pCur = kLdrDyldHead; pCur; pCur = pCur->Load.pNext) { if ( !pCur->fGlobalOrSpecific && pCur->pMod->cchFilename == cchFilename && !kLdrHlpMemComp(pCur->pMod->pszFilename, pszFilename, cchFilename)) { kLdrRdrClose(pRdr); kldrDyldModMarkGlobal(pCur); *ppMod = pCur; return 0; } KLDRDYLDFIND_ASSERT( pCur->pMod->cchFilename != cchFilename || kLdrHlpMemComp(pCur->pMod->pszFilename, pszFilename, cchFilename)); } } /* * Create a new module. */ rc = kldrDyldModCreate(pRdr, fFlags, ppMod); if (rc) kLdrRdrClose(pRdr); } return rc; } /** * Searches for a DLL file using the specified method. * * @returns 0 on success and *ppMod pointing to the new module. * @returns KLDR_ERR_MODULE_NOT_FOUND if the specified file couldn't be opened. * @returns non-zero kLdr or OS specific status code on other failures. * @param pszName The name. * @param pszPrefix The prefix, optional. * @param pszSuffix The suffix, optional. * @param enmSearch The search method. * @param fFlags The load/search flags. * @param ppRdr Where to store the pointer to the file provider instance on success. */ static int kldrDyldFindDoDllSearch(const char *pszName, const char *pszPrefix, const char *pszSuffix, KLDRDYLDSEARCH enmSearch, unsigned fFlags, PPKLDRRDR ppRdr) { int rc; KLDRDYLDFINDARGS Args; /* * Initialize the argument structure and resolve defaults. */ Args.enmSearch = enmSearch; Args.pszPrefix = pszPrefix; Args.pszSuffix = pszSuffix; rc = kldrDyldFindGetDefaults(&Args.enmSearch, &Args.pszPrefix, &Args.pszSuffix, pszName, fFlags); if (rc) return rc; Args.pszName = pszName; Args.cchName = kLdrHlpStrLen(pszName); Args.cchPrefix = Args.pszPrefix ? kLdrHlpStrLen(Args.pszPrefix) : 0; Args.cchSuffix = Args.pszSuffix ? kLdrHlpStrLen(Args.pszSuffix) : 0; Args.cchMaxLength = Args.cchName + Args.cchSuffix + Args.cchPrefix; Args.fFlags = fFlags; Args.ppRdr = ppRdr; /* * Apply the specified search method. */ /** @todo get rid of the strlen() on the various paths here! */ switch (Args.enmSearch) { case KLDRDYLD_SEARCH_KLDR: { kldrDyldFindLazyInitAppDir(); if (kLdrDyldAppDir[0] != '\0') { rc = kldrDyldFindTryOpenPath(kLdrDyldAppDir, kLdrHlpStrLen(kLdrDyldAppDir), &Args); if (rc != KLDR_ERR_MODULE_NOT_FOUND) break; } rc = kldrDyldFindTryOpenPath(".", 1, &Args); if (rc != KLDR_ERR_MODULE_NOT_FOUND) break; rc = kldrDyldFindEnumeratePath(kLdrDyldLibraryPath, &Args); break; } case KLDRDYLD_SEARCH_OS2: { rc = kldrDyldFindEnumeratePath(kLdrDyldOS2BeginLibpath, &Args); if (rc != KLDR_ERR_MODULE_NOT_FOUND) break; rc = kldrDyldFindEnumeratePath(kLdrDyldOS2Libpath, &Args); if (rc != KLDR_ERR_MODULE_NOT_FOUND) break; rc = kldrDyldFindEnumeratePath(kLdrDyldOS2EndLibpath, &Args); break; } case KLDRDYLD_SEARCH_WINDOWS: case KLDRDYLD_SEARCH_WINDOWS_ALTERED: { kldrDyldFindLazyInitAppDir(); rc = kldrDyldFindTryOpenPath(kLdrDyldWindowsAppDir, kLdrHlpStrLen(kLdrDyldWindowsAppDir), &Args); if (rc != KLDR_ERR_MODULE_NOT_FOUND) break; if (Args.enmSearch == KLDRDYLD_SEARCH_WINDOWS_ALTERED) { rc = kldrDyldFindTryOpenPath(".", 1, &Args); if (rc != KLDR_ERR_MODULE_NOT_FOUND) break; } rc = kldrDyldFindTryOpenPath(kLdrDyldWindowsSystemDir, kLdrHlpStrLen(kLdrDyldWindowsSystemDir), &Args); if (rc != KLDR_ERR_MODULE_NOT_FOUND) break; rc = kldrDyldFindTryOpenPath(kLdrDyldWindowsDir, kLdrHlpStrLen(kLdrDyldWindowsDir), &Args); if (rc != KLDR_ERR_MODULE_NOT_FOUND) break; if (Args.enmSearch == KLDRDYLD_SEARCH_WINDOWS) { rc = kldrDyldFindTryOpenPath(".", 1, &Args); if (rc != KLDR_ERR_MODULE_NOT_FOUND) break; } rc = kldrDyldFindEnumeratePath(kLdrDyldWindowsPath, &Args); break; } case KLDRDYLD_SEARCH_UNIX_COMMON: { rc = kldrDyldFindEnumeratePath(kLdrDyldUnixLibraryPath, &Args); if (rc == KLDR_ERR_MODULE_NOT_FOUND) break; rc = kldrDyldFindEnumeratePath(kLdrDyldUnixSystemLibraryPath, &Args); break; } default: kldrHlpAssert(!"internal error"); return -1; } return rc; } /** * Searches for an EXE file using the specified method. * * @returns 0 on success and *ppMod pointing to the new module. * @returns KLDR_ERR_MODULE_NOT_FOUND if the specified file couldn't be opened. * @returns non-zero kLdr or OS specific status code on other failures. * @param pszName The name. * @param pszPrefix The prefix, optional. * @param pszSuffix The suffix, optional. * @param enmSearch The search method. * @param fFlags The load/search flags. * @param ppRdr Where to store the pointer to the file provider instance on success. */ static int kldrDyldFindDoExeSearch(const char *pszName, const char *pszPrefix, const char *pszSuffix, KLDRDYLDSEARCH enmSearch, unsigned fFlags, PPKLDRRDR ppRdr) { int rc; KLDRDYLDFINDARGS Args; /* * Initialize the argument structure and resolve defaults. */ Args.enmSearch = enmSearch; Args.pszPrefix = pszPrefix; Args.pszSuffix = pszSuffix; rc = kldrDyldFindGetDefaults(&Args.enmSearch, &Args.pszPrefix, &Args.pszSuffix, pszName, fFlags); if (rc) return rc; Args.pszName = pszName; Args.cchName = kLdrHlpStrLen(pszName); Args.cchPrefix = Args.pszPrefix ? kLdrHlpStrLen(Args.pszPrefix) : 0; Args.cchSuffix = Args.pszSuffix ? kLdrHlpStrLen(Args.pszSuffix) : 0; Args.cchMaxLength = Args.cchName + Args.cchSuffix + Args.cchPrefix; Args.fFlags = fFlags; Args.ppRdr = ppRdr; /* * If we're bootstrapping a process, we'll start by looking in the * application directory and the check out the path. */ if (g_fBootstrapping) { kldrDyldFindLazyInitAppDir(); if (kLdrDyldAppDir[0] != '\0') { rc = kldrDyldFindTryOpenPath(kLdrDyldAppDir, kLdrHlpStrLen(kLdrDyldAppDir), &Args); if (rc != KLDR_ERR_MODULE_NOT_FOUND) return rc; } } /* * Search the EXE search path. Initialize it the first time around. */ if (!kLdrDyldExePath[0]) { size_t cch; kldrHlpGetEnv("KLDR_EXE_PATH", kLdrDyldExePath, sizeof(kLdrDyldExePath) - 10); cch = kLdrHlpStrLen(kLdrDyldExePath); kLdrDyldExePath[cch++] = ';'; kldrHlpGetEnv("PATH", &kLdrDyldExePath[cch], sizeof(kLdrDyldExePath) - cch); } return kldrDyldFindEnumeratePath(kLdrDyldExePath, &Args); } /** * Try open the specfied file. * * @returns 0 on success and *ppMod pointing to the new module. * @returns KLDR_ERR_MODULE_NOT_FOUND if the specified file couldn't be opened. * @returns non-zero kLdr or OS specific status code on other failures. * @param pszFilename The filename. * @param ppRdr Where to store the pointer to the new module. */ static int kldrDyldFindTryOpen(const char *pszFilename, PPKLDRRDR ppRdr) { int rc; /* * Try open the file. */ rc = kLdrRdrOpen(ppRdr, pszFilename); if (!rc) return 0; /** @todo deal with return codes properly. */ if (rc >= KLDR_ERR_BASE && rc <= KLDR_ERR_END) return rc; return KLDR_ERR_MODULE_NOT_FOUND; } /** * Composes a filename from the specified directory path, * prefix (optional), name and suffix (optional, will try with and without). * * @param pchPath The directory path - this doesn't have to be null terminated. * @param cchPath The length of the path. * @param pArgs The search argument structure. * * @returns See kldrDyldFindTryOpen */ static int kldrDyldFindTryOpenPath(const char *pchPath, size_t cchPath, PCKLDRDYLDFINDARGS pArgs) { static char s_szFilename[1024]; char *psz; int rc; /* * Ignore any attempts at opening empty paths. * This can happen when a *Dir globals is empty. */ if (!cchPath) return KLDR_ERR_MODULE_NOT_FOUND; /* ignore */ /* * Limit check first. */ if (cchPath + 1 + pArgs->cchMaxLength >= sizeof(s_szFilename)) { KLDRDYLDFIND_ASSERT(!"too long"); return KLDR_ERR_MODULE_NOT_FOUND; /* ignore */ } /* * The directory path. */ kLdrHlpMemCopy(s_szFilename, pchPath, cchPath); psz = &s_szFilename[cchPath]; if (psz[-1] != '/' #if defined(__OS2__) || defined(__WIN__) && psz[-1] != '\\' && psz[-1] != ':' #endif ) *psz++ = '/'; /* * The name. */ if (pArgs->cchPrefix) { kLdrHlpMemCopy(psz, pArgs->pszPrefix, pArgs->cchPrefix); psz += pArgs->cchPrefix; } kLdrHlpMemCopy(psz, pArgs->pszName, pArgs->cchName); psz += pArgs->cchName; if (pArgs->cchSuffix) { kLdrHlpMemCopy(psz, pArgs->pszSuffix, pArgs->cchSuffix); psz += pArgs->cchSuffix; } *psz = '\0'; /* * Try open it. */ rc = kldrDyldFindTryOpen(s_szFilename, pArgs->ppRdr); /* If we're opening an executable, try again without the suffix.*/ if ( rc && pArgs->cchSuffix && (pArgs->fFlags & KLDRDYLD_LOAD_FLAGS_EXECUTABLE)) { psz -= pArgs->cchSuffix; *psz = '\0'; rc = kldrDyldFindTryOpen(s_szFilename, pArgs->ppRdr); } return rc; } /** * Enumerates the specfied path. * * @returns Any return code from the kldrDyldFindTryOpenPath() which isn't KLDR_ERR_MODULE_NOT_FOUND. * @returns KLDR_ERR_MODULE_NOT_FOUND if the end of the search path was reached. * @param pszSearchPath The search path to enumeare. * @param pArgs The search argument structure. */ static int kldrDyldFindEnumeratePath(const char *pszSearchPath, PCKLDRDYLDFINDARGS pArgs) { const char *psz = pszSearchPath; for (;;) { const char *pszEnd; size_t cchPath; /* * Trim. */ while (*psz == ';' || *psz == ':') psz++; if (*psz == '\0') return KLDR_ERR_MODULE_NOT_FOUND; /* * Find the end. */ pszEnd = psz + 1; while ( *pszEnd != '\0' && *pszEnd != ';' #if defined(__OS2__) || defined(__WIN__) && ( *pszEnd != ':' || ( pszEnd - psz == 1 && ( (*psz >= 'A' && *psz <= 'Z') || (*psz >= 'a' && *psz <= 'z') ) ) ) #else && *pszEnd != ':' #endif ) pszEnd++; /* * If not empty path, try open the module using it. */ cchPath = pszEnd - psz; if (cchPath > 0) { int rc; rc = kldrDyldFindTryOpenPath(psz, cchPath, pArgs); if (rc != KLDR_ERR_MODULE_NOT_FOUND) return rc; } /* next */ psz = pszEnd; } } /** * Resolve default search method, prefix and suffix. * * @returns 0 on success, KLDR_ERR_INVALID_PARAMETER on failure. * @param penmSearch The search method. In/Out. * @param ppszPrefix The prefix. In/Out. * @param ppszSuffix The suffix. In/Out. * @param pszName The name. In. * @param fFlags The load/search flags. */ static int kldrDyldFindGetDefaults(KLDRDYLDSEARCH *penmSearch, const char **ppszPrefix, const char **ppszSuffix, const char *pszName, uint32_t fFlags) { unsigned fCaseSensitive; /* * Fixup search method alias. */ if (*penmSearch == KLDRDYLD_SEARCH_HOST) #if defined(__OS2__) *penmSearch = KLDRDYLD_SEARCH_OS2; #elif defined(__WIN__) *penmSearch = KLDRDYLD_SEARCH_WINDOWS; #else # error "Port me" #endif /* * Apply search method specific prefix/suffix. */ switch (*penmSearch) { case KLDRDYLD_SEARCH_KLDR: if (!*ppszPrefix && kLdrDyldDefPrefix[0]) *ppszPrefix = kLdrDyldDefPrefix; if (!*ppszSuffix && kLdrDyldDefSuffix[0]) *ppszSuffix = kLdrDyldDefSuffix; fCaseSensitive = 1; break; case KLDRDYLD_SEARCH_OS2: if (!*ppszSuffix) *ppszSuffix = !(fFlags & KLDRDYLD_LOAD_FLAGS_EXECUTABLE) ? ".dll" : ".exe"; fCaseSensitive = 0; break; case KLDRDYLD_SEARCH_WINDOWS: case KLDRDYLD_SEARCH_WINDOWS_ALTERED: if (!*ppszSuffix) *ppszSuffix = !(fFlags & KLDRDYLD_LOAD_FLAGS_EXECUTABLE) ? ".dll" : ".exe"; fCaseSensitive = 0; break; case KLDRDYLD_SEARCH_UNIX_COMMON: fCaseSensitive = 1; break; default: KLDRDYLDFIND_ASSERT(!"invalid search method"); return KLDR_ERR_INVALID_PARAMETER; } /* * Drop the suffix if it's already included in the name. */ if (*ppszSuffix) { const size_t cchName = kLdrHlpStrLen(pszName); const size_t cchSuffix = kLdrHlpStrLen(*ppszSuffix); if ( cchName > cchSuffix && ( fCaseSensitive ? !kLdrHlpMemComp(pszName + cchName - cchSuffix, *ppszSuffix, cchSuffix) : !kLdrHlpMemIComp(pszName + cchName - cchSuffix, *ppszSuffix, cchSuffix)) ) *ppszSuffix = NULL; } return 0; } /** * Locates an already open module using the specified search method. * * @returns 0 and *ppMod on success, non-zero OS specific error on failure. * * @param pszName Partial or complete name, it's specific to the search method to determin which. * @param pszPrefix Prefix than can be used when searching. * @param pszSuffix Suffix than can be used when searching. * @param enmSearch The file search method to apply. * @param fFlags Search flags. * @param ppMod Where to store the file provider instance on success. */ int kldrDyldFindExistingModule(const char *pszName, const char *pszPrefix, const char *pszSuffix, KLDRDYLDSEARCH enmSearch, unsigned fFlags, PPKLDRDYLDMOD ppMod) { int rc; unsigned fOS2LibpathStrict; *ppMod = NULL; /* * Don't bother if no modules are loaded yet. */ if (!kLdrDyldHead) return KLDR_ERR_MODULE_NOT_FOUND; /* * Defaults. */ rc = kldrDyldFindGetDefaults(&enmSearch, &pszPrefix, &pszSuffix, pszName, fFlags); if (rc) return rc; /* * If this isn't just a filename, the caller has specified a file * that should be opened directly and not a module name to be searched for. * * In order to do the right thing we'll have to open the file and get the * correct filename for it. * * The OS/2 libpath strict method require us to find the correct DLL first. */ fOS2LibpathStrict = 0; if ( !kldrHlpIsFilenameOnly(pszName) || (fOS2LibpathStrict = ( enmSearch == KLDRDYLD_SEARCH_OS2 && kLdrDyldOS2LibpathStrict[0] == 'T') ) ) { PKLDRRDR pRdr; if (fOS2LibpathStrict) rc = kldrDyldFindDoDllSearch(pszName, pszPrefix, pszSuffix, enmSearch, fFlags, &pRdr); else rc = kldrDyldFindTryOpen(pszName, &pRdr); if (!rc) { /* do a filename based search. */ const char *pszFilename = kLdrRdrName(pRdr); const size_t cchFilename = kLdrHlpStrLen(pszFilename); PKLDRDYLDMOD pCur; rc = KLDR_ERR_MODULE_NOT_FOUND; for (pCur = kLdrDyldHead; pCur; pCur = pCur->Load.pNext) { if ( pCur->pMod->cchFilename == cchFilename && !kLdrHlpMemComp(pCur->pMod->pszFilename, pszFilename, cchFilename)) { *ppMod = pCur; rc = 0; break; } } kLdrRdrClose(pRdr); } } else { const size_t cchName = kLdrHlpStrLen(pszName); const size_t cchPrefix = pszPrefix ? kLdrHlpStrLen(pszPrefix) : 0; const size_t cchSuffix = pszSuffix ? kLdrHlpStrLen(pszSuffix) : 0; const char *pszNameSuffix = kldrHlpGetSuff(pszName); PKLDRDYLDMOD pCur = kLdrDyldHead; /* * Some of the methods are case insensitive (ASCII), others are case sensitive. * To avoid having todo indirect calls to the compare functions here, we split * ways even if it means a lot of duplicate code. */ if ( enmSearch == KLDRDYLD_SEARCH_OS2 || enmSearch == KLDRDYLD_SEARCH_WINDOWS || enmSearch == KLDRDYLD_SEARCH_WINDOWS_ALTERED) { const unsigned fNameHasSuffix = pszNameSuffix && kLdrHlpStrLen(pszNameSuffix) == cchSuffix && !kLdrHlpMemIComp(pszNameSuffix, pszName + cchName - cchSuffix, cchSuffix); for (; pCur; pCur = pCur->Load.pNext) { /* match global / specific */ if ( !pCur->fGlobalOrSpecific && !(fFlags & KLDRYDLD_LOAD_FLAGS_SPECIFIC_MODULE)) continue; /* match name */ if ( pCur->pMod->cchName == cchName && !kLdrHlpMemIComp(pCur->pMod->pszName, pszName, cchName)) break; if (cchPrefix) { if ( pCur->pMod->cchName == cchName + cchPrefix && !kLdrHlpMemIComp(pCur->pMod->pszName, pszPrefix, cchPrefix) && !kLdrHlpMemIComp(pCur->pMod->pszName + cchPrefix, pszName, cchName)) break; } if (cchSuffix) { if ( pCur->pMod->cchName == cchName + cchSuffix && !kLdrHlpMemIComp(pCur->pMod->pszName + cchName, pszSuffix, cchSuffix) && !kLdrHlpMemIComp(pCur->pMod->pszName, pszName, cchName)) break; if ( fNameHasSuffix && pCur->pMod->cchName == cchName - cchSuffix && !kLdrHlpMemIComp(pCur->pMod->pszName, pszName, cchName - cchSuffix)) break; if (cchPrefix) { if ( pCur->pMod->cchName == cchName + cchPrefix + cchSuffix && !kLdrHlpMemIComp(pCur->pMod->pszName, pszPrefix, cchPrefix) && !kLdrHlpMemIComp(pCur->pMod->pszName + cchPrefix, pszName, cchName) && !kLdrHlpMemIComp(pCur->pMod->pszName + cchPrefix + cchName, pszSuffix, cchSuffix)) break; if ( fNameHasSuffix && pCur->pMod->cchName == cchName + cchPrefix - cchSuffix && !kLdrHlpMemIComp(pCur->pMod->pszName, pszPrefix, cchPrefix) && !kLdrHlpMemIComp(pCur->pMod->pszName + cchPrefix, pszName, cchName - cchSuffix)) break; } } } } else { const unsigned fNameHasSuffix = pszNameSuffix && kLdrHlpStrLen(pszNameSuffix) == cchSuffix && kLdrHlpMemComp(pszNameSuffix, pszName + cchName - cchSuffix, cchSuffix); for (; pCur; pCur = pCur->Load.pNext) { /* match global / specific */ if ( !pCur->fGlobalOrSpecific && !(fFlags & KLDRYDLD_LOAD_FLAGS_SPECIFIC_MODULE)) continue; /* match name */ if ( pCur->pMod->cchName == cchName && !kLdrHlpMemComp(pCur->pMod->pszName, pszName, cchName)) break; if (cchPrefix) { if ( pCur->pMod->cchName == cchName + cchPrefix && !kLdrHlpMemComp(pCur->pMod->pszName, pszPrefix, cchPrefix) && !kLdrHlpMemComp(pCur->pMod->pszName + cchPrefix, pszName, cchName)) break; } if (cchSuffix) { if ( pCur->pMod->cchName == cchName + cchSuffix && !kLdrHlpMemComp(pCur->pMod->pszName + cchName, pszSuffix, cchSuffix) && !kLdrHlpMemComp(pCur->pMod->pszName, pszName, cchName)) break; if ( fNameHasSuffix && pCur->pMod->cchName == cchName - cchSuffix && !kLdrHlpMemComp(pCur->pMod->pszName, pszName, cchName - cchSuffix)) break; if (cchPrefix) { if ( pCur->pMod->cchName == cchName + cchPrefix + cchSuffix && !kLdrHlpMemComp(pCur->pMod->pszName, pszPrefix, cchPrefix) && !kLdrHlpMemComp(pCur->pMod->pszName + cchPrefix, pszName, cchName) && !kLdrHlpMemComp(pCur->pMod->pszName + cchPrefix + cchName, pszSuffix, cchSuffix)) break; if ( pCur->pMod->cchName == cchName + cchPrefix - cchSuffix && !kLdrHlpMemComp(pCur->pMod->pszName, pszPrefix, cchPrefix) && !kLdrHlpMemComp(pCur->pMod->pszName + cchPrefix, pszName, cchName - cchSuffix)) break; } } } } /* search result. */ if (pCur) { *ppMod = pCur; rc = 0; } else rc = KLDR_ERR_MODULE_NOT_FOUND; } return rc; }