summaryrefslogtreecommitdiffstats
path: root/src/lib/kStuff/kLdr/kLdrDyldFind.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/kStuff/kLdr/kLdrDyldFind.c')
-rw-r--r--src/lib/kStuff/kLdr/kLdrDyldFind.c1086
1 files changed, 1086 insertions, 0 deletions
diff --git a/src/lib/kStuff/kLdr/kLdrDyldFind.c b/src/lib/kStuff/kLdr/kLdrDyldFind.c
new file mode 100644
index 0000000..9d6562e
--- /dev/null
+++ b/src/lib/kStuff/kLdr/kLdrDyldFind.c
@@ -0,0 +1,1086 @@
+/* $Id: kLdrDyldFind.c 29 2009-07-01 20:30:29Z bird $ */
+/** @file
+ * kLdr - The Dynamic Loader, File Searching 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"
+
+#if K_OS == K_OS_LINUX
+# include <k/kHlpSys.h>
+
+#elif K_OS == K_OS_OS2
+# define INCL_BASE
+# define INCL_ERRORS
+# include <os2.h>
+# 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 K_OS == K_OS_WINDOWS
+# undef IMAGE_DOS_SIGNATURE
+# undef IMAGE_NT_SIGNATURE
+# include <Windows.h>
+
+#endif
+
+
+/*******************************************************************************
+* 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) kHlpAssert(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;
+ KSIZE cchName;
+
+ const char *pszPrefix;
+ KSIZE cchPrefix;
+
+ const char *pszSuffix;
+ KSIZE cchSuffix;
+
+ KSIZE cchMaxLength;
+
+ KLDRDYLDSEARCH enmSearch;
+ KU32 fFlags;
+ PPKRDR 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, PPKRDR ppRdr);
+static int kldrDyldFindDoExeSearch(const char *pszName, const char *pszPrefix, const char *pszSuffix,
+ KLDRDYLDSEARCH enmSearch, unsigned fFlags, PPKRDR ppRdr);
+static int kldrDyldFindTryOpen(const char *pszFilename, PPKRDR ppRdr);
+static int kldrDyldFindTryOpenPath(const char *pchPath, KSIZE 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, KU32 fFlags);
+
+
+/**
+ * Initializes the find paths.
+ *
+ * @returns 0 on success, non-zero on failure.
+ */
+int kldrDyldFindInit(void)
+{
+ KSIZE cch;
+ int rc;
+ char szTmp[sizeof(kLdrDyldDefSuffix)];
+
+ /*
+ * The kLdr search parameters.
+ */
+ rc = kHlpGetEnv("KLDR_LIBRARY_PATH", kLdrDyldLibraryPath, sizeof(kLdrDyldLibraryPath));
+ rc = kHlpGetEnv("KLDR_DEF_PREFIX", szTmp, sizeof(szTmp));
+ if (!rc)
+ kHlpMemCopy(kLdrDyldDefPrefix, szTmp, sizeof(szTmp));
+ rc = kHlpGetEnv("KLDR_DEF_SUFFIX", szTmp, sizeof(szTmp));
+ if (!rc)
+ kHlpMemCopy(kLdrDyldDefSuffix, szTmp, sizeof(szTmp));
+
+ /*
+ * The OS/2 search parameters.
+ */
+#if K_OS == K_OS_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
+ kHlpGetEnv("KLDR_OS2_LIBPATH", kLdrDyldOS2Libpath, sizeof(kLdrDyldOS2Libpath));
+ kHlpGetEnv("KLDR_OS2_LIBPATHSTRICT", kLdrDyldOS2LibpathStrict, sizeof(kLdrDyldOS2LibpathStrict));
+ if ( kLdrDyldOS2LibpathStrict[0] == 'T'
+ || kLdrDyldOS2LibpathStrict[0] == 't')
+ kLdrDyldOS2LibpathStrict[0] = 'T';
+ else
+ kLdrDyldOS2LibpathStrict[0] = '\0';
+ kLdrDyldOS2LibpathStrict[1] = '\0';
+ kHlpGetEnv("KLDR_OS2_BEGINLIBPATH", kLdrDyldOS2BeginLibpath, sizeof(kLdrDyldOS2BeginLibpath));
+ kHlpGetEnv("KLDR_OS2_ENDLIBPATH", kLdrDyldOS2EndLibpath, sizeof(kLdrDyldOS2EndLibpath));
+#endif
+
+ /*
+ * The windows search parameters.
+ */
+#if K_OS == K_OS_WINDOWS
+ 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;
+ kHlpGetEnv("PATH", kLdrDyldWindowsPath, sizeof(kLdrDyldWindowsPath));
+#else
+ kHlpGetEnv("KLDR_WINDOWS_SYSTEM_DIR", kLdrDyldWindowsSystemDir, sizeof(kLdrDyldWindowsSystemDir));
+ kHlpGetEnv("KLDR_WINDOWS_DIR", kLdrDyldWindowsDir, sizeof(kLdrDyldWindowsDir));
+ rc = kHlpGetEnv("KLDR_WINDOWS_PATH", kLdrDyldWindowsPath, sizeof(kLdrDyldWindowsPath));
+ if (rc)
+ kHlpGetEnv("PATH", kLdrDyldWindowsPath, sizeof(kLdrDyldWindowsPath));
+#endif
+
+ /*
+ * The Unix search parameters.
+ */
+ rc = kHlpGetEnv("KLDR_UNIX_LIBRARY_PATH", kLdrDyldUnixLibraryPath, sizeof(kLdrDyldUnixLibraryPath));
+ if (rc)
+ kHlpGetEnv("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 K_OS == K_OS_DARWIN
+ /** @todo implement this! */
+ kLdrDyldWindowsAppDir[0] = kLdrDyldAppDir[0] = '.';
+ kLdrDyldWindowsAppDir[1] = kLdrDyldAppDir[1] = '\0';
+
+#elif K_OS == K_OS_LINUX
+ KSSIZE cch = kHlpSys_readlink("/proc/self/exe", kLdrDyldAppDir, sizeof(kLdrDyldAppDir) - 1);
+ if (cch > 0)
+ {
+ kLdrDyldAppDir[cch] = '\0';
+ *kHlpGetFilename(kLdrDyldAppDir) = '\0';
+ kHlpMemCopy(kLdrDyldWindowsAppDir, kLdrDyldAppDir, sizeof(kLdrDyldAppDir));
+ }
+ else
+ {
+ kLdrDyldWindowsAppDir[0] = kLdrDyldAppDir[0] = '.';
+ kLdrDyldWindowsAppDir[1] = kLdrDyldAppDir[1] = '\0';
+ }
+
+#elif K_OS == K_OS_OS2
+ PPIB pPib;
+ PTIB pTib;
+ APIRET rc;
+
+ DosGetInfoBlocks(&pTib, &pPib);
+ rc = DosQueryModuleName(pPib->pib_hmte, sizeof(kLdrDyldAppDir), kLdrDyldAppDir);
+ if (!rc)
+ {
+ *kHlpGetFilename(kLdrDyldAppDir) = '\0';
+ kHlpMemCopy(kLdrDyldWindowsAppDir, kLdrDyldAppDir, sizeof(kLdrDyldAppDir));
+ }
+ else
+ {
+ kLdrDyldWindowsAppDir[0] = kLdrDyldAppDir[0] = '.';
+ kLdrDyldWindowsAppDir[1] = kLdrDyldAppDir[1] = '\0';
+ }
+
+#elif K_OS == K_OS_WINDOWS
+ DWORD dwSize = GetModuleFileName(NULL /* the executable */, kLdrDyldAppDir, sizeof(kLdrDyldAppDir));
+ if (dwSize > 0)
+ {
+ *kHlpGetFilename(kLdrDyldAppDir) = '\0';
+ kHlpMemCopy(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;
+ PKRDR 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 (!kHlpIsFilenameOnly(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 = kRdrName(pRdr);
+ const KSIZE cchFilename = kHlpStrLen(pszFilename);
+ PKLDRDYLDMOD pCur;
+ for (pCur = kLdrDyldHead; pCur; pCur = pCur->Load.pNext)
+ KLDRDYLDFIND_ASSERT( pCur->pMod->cchFilename != cchFilename
+ || kHlpMemComp(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 = kRdrName(pRdr);
+ const KSIZE cchFilename = kHlpStrLen(pszFilename);
+ PKLDRDYLDMOD pCur;
+ for (pCur = kLdrDyldHead; pCur; pCur = pCur->Load.pNext)
+ {
+ if ( !pCur->fGlobalOrSpecific
+ && pCur->pMod->cchFilename == cchFilename
+ && !kHlpMemComp(pCur->pMod->pszFilename, pszFilename, cchFilename))
+ {
+ kRdrClose(pRdr);
+ kldrDyldModMarkGlobal(pCur);
+ *ppMod = pCur;
+ return 0;
+ }
+ KLDRDYLDFIND_ASSERT( pCur->pMod->cchFilename != cchFilename
+ || kHlpMemComp(pCur->pMod->pszFilename, pszFilename, cchFilename));
+ }
+ }
+
+ /*
+ * Create a new module.
+ */
+ rc = kldrDyldModCreate(pRdr, fFlags, ppMod);
+ if (rc)
+ kRdrClose(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, PPKRDR 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 = kHlpStrLen(pszName);
+ Args.cchPrefix = Args.pszPrefix ? kHlpStrLen(Args.pszPrefix) : 0;
+ Args.cchSuffix = Args.pszSuffix ? kHlpStrLen(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, kHlpStrLen(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, kHlpStrLen(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, kHlpStrLen(kLdrDyldWindowsSystemDir), &Args);
+ if (rc != KLDR_ERR_MODULE_NOT_FOUND)
+ break;
+ rc = kldrDyldFindTryOpenPath(kLdrDyldWindowsDir, kHlpStrLen(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: kHlpAssert(!"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, PPKRDR 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 = kHlpStrLen(pszName);
+ Args.cchPrefix = Args.pszPrefix ? kHlpStrLen(Args.pszPrefix) : 0;
+ Args.cchSuffix = Args.pszSuffix ? kHlpStrLen(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, kHlpStrLen(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])
+ {
+ KSIZE cch;
+ kHlpGetEnv("KLDR_EXE_PATH", kLdrDyldExePath, sizeof(kLdrDyldExePath) - 10);
+ cch = kHlpStrLen(kLdrDyldExePath);
+ kLdrDyldExePath[cch++] = ';';
+ kHlpGetEnv("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, PPKRDR ppRdr)
+{
+ int rc;
+
+ /*
+ * Try open the file.
+ */
+ rc = kRdrOpen(ppRdr, pszFilename);
+ if (!rc)
+ return 0;
+ /** @todo deal with return codes properly. */
+ if (rc >= KERR_BASE && rc <= KERR_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, KSIZE 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.
+ */
+ kHlpMemCopy(s_szFilename, pchPath, cchPath);
+ psz = &s_szFilename[cchPath];
+ if (psz[-1] != '/'
+#if K_OS == K_OS_OS2 || K_OS == K_OS_WINDOWS
+ && psz[-1] != '\\'
+ && psz[-1] != ':'
+#endif
+ )
+ *psz++ = '/';
+
+ /*
+ * The name.
+ */
+ if (pArgs->cchPrefix)
+ {
+ kHlpMemCopy(psz, pArgs->pszPrefix, pArgs->cchPrefix);
+ psz += pArgs->cchPrefix;
+ }
+ kHlpMemCopy(psz, pArgs->pszName, pArgs->cchName);
+ psz += pArgs->cchName;
+ if (pArgs->cchSuffix)
+ {
+ kHlpMemCopy(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;
+ KSIZE 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 K_OS == K_OS_OS2 || K_OS == K_OS_WINDOWS
+ && ( *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, KERR_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, KU32 fFlags)
+{
+ unsigned fCaseSensitive;
+
+ /*
+ * Fixup search method alias.
+ */
+ if (*penmSearch == KLDRDYLD_SEARCH_HOST)
+#if K_OS == K_OS_DARWIN
+ /** @todo *penmSearch = KLDRDYLD_SEARCH_DARWIN; */
+ *penmSearch = KLDRDYLD_SEARCH_UNIX_COMMON;
+#elif K_OS == K_OS_FREEBSD \
+ || K_OS == K_OS_LINUX \
+ || K_OS == K_OS_NETBSD \
+ || K_OS == K_OS_OPENBSD \
+ || K_OS == K_OS_SOLARIS
+ *penmSearch = KLDRDYLD_SEARCH_UNIX_COMMON;
+#elif K_OS == K_OS_OS2
+ *penmSearch = KLDRDYLD_SEARCH_OS2;
+#elif K_OS == K_OS_WINDOWS
+ *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 KERR_INVALID_PARAMETER;
+ }
+
+ /*
+ * Drop the suffix if it's already included in the name.
+ */
+ if (*ppszSuffix)
+ {
+ const KSIZE cchName = kHlpStrLen(pszName);
+ const KSIZE cchSuffix = kHlpStrLen(*ppszSuffix);
+ if ( cchName > cchSuffix
+ && ( fCaseSensitive
+ ? !kHlpMemComp(pszName + cchName - cchSuffix, *ppszSuffix, cchSuffix)
+ : !kHlpMemICompAscii(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 ( !kHlpIsFilenameOnly(pszName)
+ || (fOS2LibpathStrict = ( enmSearch == KLDRDYLD_SEARCH_OS2
+ && kLdrDyldOS2LibpathStrict[0] == 'T')
+ )
+ )
+ {
+ PKRDR 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 = kRdrName(pRdr);
+ const KSIZE cchFilename = kHlpStrLen(pszFilename);
+ PKLDRDYLDMOD pCur;
+ rc = KLDR_ERR_MODULE_NOT_FOUND;
+ for (pCur = kLdrDyldHead; pCur; pCur = pCur->Load.pNext)
+ {
+ if ( pCur->pMod->cchFilename == cchFilename
+ && !kHlpMemComp(pCur->pMod->pszFilename, pszFilename, cchFilename))
+ {
+ *ppMod = pCur;
+ rc = 0;
+ break;
+ }
+ }
+ kRdrClose(pRdr);
+ }
+ }
+ else
+ {
+ const KSIZE cchName = kHlpStrLen(pszName);
+ const KSIZE cchPrefix = pszPrefix ? kHlpStrLen(pszPrefix) : 0;
+ const KSIZE cchSuffix = pszSuffix ? kHlpStrLen(pszSuffix) : 0;
+ const char *pszNameSuffix = kHlpGetSuff(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
+ && kHlpStrLen(pszNameSuffix) == cchSuffix
+ && !kHlpMemICompAscii(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
+ && !kHlpMemICompAscii(pCur->pMod->pszName, pszName, cchName))
+ break;
+ if (cchPrefix)
+ {
+ if ( pCur->pMod->cchName == cchName + cchPrefix
+ && !kHlpMemICompAscii(pCur->pMod->pszName, pszPrefix, cchPrefix)
+ && !kHlpMemICompAscii(pCur->pMod->pszName + cchPrefix, pszName, cchName))
+ break;
+ }
+ if (cchSuffix)
+ {
+ if ( pCur->pMod->cchName == cchName + cchSuffix
+ && !kHlpMemICompAscii(pCur->pMod->pszName + cchName, pszSuffix, cchSuffix)
+ && !kHlpMemICompAscii(pCur->pMod->pszName, pszName, cchName))
+ break;
+ if ( fNameHasSuffix
+ && pCur->pMod->cchName == cchName - cchSuffix
+ && !kHlpMemICompAscii(pCur->pMod->pszName, pszName, cchName - cchSuffix))
+ break;
+ if (cchPrefix)
+ {
+ if ( pCur->pMod->cchName == cchName + cchPrefix + cchSuffix
+ && !kHlpMemICompAscii(pCur->pMod->pszName, pszPrefix, cchPrefix)
+ && !kHlpMemICompAscii(pCur->pMod->pszName + cchPrefix, pszName, cchName)
+ && !kHlpMemICompAscii(pCur->pMod->pszName + cchPrefix + cchName, pszSuffix, cchSuffix))
+ break;
+ if ( fNameHasSuffix
+ && pCur->pMod->cchName == cchName + cchPrefix - cchSuffix
+ && !kHlpMemICompAscii(pCur->pMod->pszName, pszPrefix, cchPrefix)
+ && !kHlpMemICompAscii(pCur->pMod->pszName + cchPrefix, pszName, cchName - cchSuffix))
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ const unsigned fNameHasSuffix = pszNameSuffix
+ && kHlpStrLen(pszNameSuffix) == cchSuffix
+ && kHlpMemComp(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
+ && !kHlpMemComp(pCur->pMod->pszName, pszName, cchName))
+ break;
+ if (cchPrefix)
+ {
+ if ( pCur->pMod->cchName == cchName + cchPrefix
+ && !kHlpMemComp(pCur->pMod->pszName, pszPrefix, cchPrefix)
+ && !kHlpMemComp(pCur->pMod->pszName + cchPrefix, pszName, cchName))
+ break;
+ }
+ if (cchSuffix)
+ {
+ if ( pCur->pMod->cchName == cchName + cchSuffix
+ && !kHlpMemComp(pCur->pMod->pszName + cchName, pszSuffix, cchSuffix)
+ && !kHlpMemComp(pCur->pMod->pszName, pszName, cchName))
+ break;
+ if ( fNameHasSuffix
+ && pCur->pMod->cchName == cchName - cchSuffix
+ && !kHlpMemComp(pCur->pMod->pszName, pszName, cchName - cchSuffix))
+ break;
+ if (cchPrefix)
+ {
+ if ( pCur->pMod->cchName == cchName + cchPrefix + cchSuffix
+ && !kHlpMemComp(pCur->pMod->pszName, pszPrefix, cchPrefix)
+ && !kHlpMemComp(pCur->pMod->pszName + cchPrefix, pszName, cchName)
+ && !kHlpMemComp(pCur->pMod->pszName + cchPrefix + cchName, pszSuffix, cchSuffix))
+ break;
+ if ( pCur->pMod->cchName == cchName + cchPrefix - cchSuffix
+ && !kHlpMemComp(pCur->pMod->pszName, pszPrefix, cchPrefix)
+ && !kHlpMemComp(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;
+}
+