From ff75702593aed447f695efa37fc3312495681f8f Mon Sep 17 00:00:00 2001 From: Michael Haubenwallner Date: Tue, 31 May 2016 13:09:11 +0000 Subject: [PATCH 1/3] dlopen: search each name within one single search dir Search x/bin:x/lib if searching in x/lib. Introduces and uses new pathfinder class, which introduces and uses new vstrlist class. --- winsup/cygwin/dlfcn.cc | 71 ++++++------- winsup/cygwin/pathfinder.h | 112 ++++++++++++++++++++ winsup/cygwin/vstrlist.h | 253 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 396 insertions(+), 40 deletions(-) create mode 100644 winsup/cygwin/pathfinder.h create mode 100644 winsup/cygwin/vstrlist.h diff --git a/winsup/cygwin/dlfcn.cc b/winsup/cygwin/dlfcn.cc index 255a6d5..da20a58 100644 --- a/winsup/cygwin/dlfcn.cc +++ b/winsup/cygwin/dlfcn.cc @@ -20,6 +20,7 @@ details. */ #include "cygtls.h" #include "tls_pbuf.h" #include "ntdll.h" +#include "pathfinder.h" static void set_dl_error (const char *str) @@ -28,29 +29,6 @@ set_dl_error (const char *str) _my_tls.locals.dl_error = 1; } -/* Look for an executable file given the name and the environment - variable to use for searching (eg., PATH); returns the full - pathname (static buffer) if found or NULL if not. */ -inline const char * -check_path_access (const char *mywinenv, const char *name, path_conv& buf) -{ - return find_exec (name, buf, mywinenv, FE_NNF | FE_DLL); -} - -/* Search LD_LIBRARY_PATH for dll, if it exists. Search /usr/bin and /usr/lib - by default. Return valid full path in path_conv real_filename. */ -static inline bool -gfpod_helper (const char *name, path_conv &real_filename) -{ - if (strchr (name, '/')) - real_filename.check (name, PC_SYM_FOLLOW | PC_NULLEMPTY); - else if (!check_path_access ("LD_LIBRARY_PATH", name, real_filename)) - check_path_access ("/usr/bin:/usr/lib", name, real_filename); - if (!real_filename.exists ()) - real_filename.error = ENOENT; - return !real_filename.error; -} - static bool get_full_path_of_dll (const char* str, path_conv &real_filename) { @@ -63,38 +41,50 @@ get_full_path_of_dll (const char* str, path_conv &real_filename) return false; /* Yes. Let caller deal with it. */ } - tmp_pathbuf tp; - char *name = tp.c_get (); + pathfinder::basenamelist basenames; - strcpy (name, str); /* Put it somewhere where we can manipulate it. */ + const char *basename = strrchr (str, '/'); + basename = basename ? basename + 1 : str; - char *basename = strrchr (name, '/'); - basename = basename ? basename + 1 : name; - char *suffix = strrchr (name, '.'); - if (suffix && suffix < basename) - suffix = NULL; + int baselen = str + len - basename; + const char *suffix = strrchr (basename, '.'); + char const * ext = ""; + int extlen = 0; /* Is suffix ".so"? */ if (suffix && !strcmp (suffix, ".so")) { /* Does the file exist? */ - if (gfpod_helper (name, real_filename)) - return true; + basenames.appendv (basename, baselen, NULL); /* No, replace ".so" with ".dll". */ - strcpy (suffix, ".dll"); + baselen -= 3; + ext = ".dll"; + extlen = 4; } /* Does the filename start with "lib"? */ if (!strncmp (basename, "lib", 3)) { /* Yes, replace "lib" with "cyg". */ - strncpy (basename, "cyg", 3); - /* Does the file exist? */ - if (gfpod_helper (name, real_filename)) - return true; + basenames.appendv ("cyg", 3, basename+3, baselen-3, ext, extlen, NULL); /* No, revert back to "lib". */ - strncpy (basename, "lib", 3); } - if (gfpod_helper (name, real_filename)) + basenames.appendv (basename, baselen, ext, extlen, NULL); + + pathfinder finder (basenames); + + if (basename > str) + finder.add_searchdir (str, basename - 1 - str); + else + { + /* NOTE: The Windows loader (for linked dlls) does + not use the LD_LIBRARY_PATH environment variable. */ + finder.add_envsearchpath ("LD_LIBRARY_PATH"); + + /* Finally we better have some fallback. */ + finder.add_searchdir ("/usr/lib", -1); + } + + if (finder.check_path_access (real_filename)) return true; /* If nothing worked, create a relative path from the original incoming @@ -113,6 +103,7 @@ dlopen (const char *name, int flags) { void *ret = NULL; + debug_printf ("flags %d for %s", flags, name); if (name == NULL) { ret = (void *) GetModuleHandle (NULL); /* handle for the current module */ diff --git a/winsup/cygwin/pathfinder.h b/winsup/cygwin/pathfinder.h new file mode 100644 index 0000000..3453692 --- /dev/null +++ b/winsup/cygwin/pathfinder.h @@ -0,0 +1,112 @@ +/* pathfinder.h: find one of multiple file names in path + + Copyright 2016 Red Hat, Inc. + +This file is part of Cygwin. + +This software is a copyrighted work licensed under the terms of the +Cygwin license. Please consult the file "CYGWIN_LICENSE" for +details. */ + +#include "vstrlist.h" + +#ifdef __cplusplus + +class pathfinder +{ +public: + typedef vstrlist basenamelist; + +private: + pathfinder (); + pathfinder (pathfinder const &); + pathfinder & operator = (pathfinder const &); + + basenamelist basenames_; + size_t basenames_maxlen_; + + typedef vstrlist searchbufferlist; + searchbufferlist searchbuffers_; + +public: + ~pathfinder () {} + + /* We need the basenames to search for first, to allow for optimized + memory allocation of each searchpath + basename combination. + The incoming list of basenames is emptied (ownership take over). */ + pathfinder (basenamelist & basenames) + : basenames_ () + , basenames_maxlen_ () + , searchbuffers_() + { + basenames.swap(basenames_); + + for (basenamelist::iterator basename = basenames_.begin (); + basename != basenames_.end (); + ++ basename) + { + if (basenames_maxlen_ < basename->stringlength ()) + basenames_maxlen_ = basename->stringlength (); + } + } + + void add_searchdir (const char *dir, int dirlen) + { + if (dirlen < 0) + dirlen = strlen (dir); + + if (!dirlen) + return; + + /* Search "x/bin:x/lib" for "x/lib" */ + if (dirlen >=4 && !strncmp (dir + dirlen - 4, "/lib", 4)) + /* prealloc buffer in searchdir for any basename we will search for */ + searchbuffers_.appendv (dir, dirlen - 4, "/bin", 4, "/", 1 + basenames_maxlen_, NULL); + + /* prealloc buffer in searchdir for any basename we will search for */ + searchbuffers_.appendv (dir, dirlen, "/", 1 + basenames_maxlen_, NULL); + } + + void add_searchpath (const char *path) + { + while (path && *path) + { + const char *next = strchr (path, ':'); + add_searchdir (path, next ? next - path : -1); + path = next ? next + 1 : next; + } + } + + void add_envsearchpath (const char *envpath) + { + add_searchpath (getenv (envpath)); + } + + /* Within each searchdir registered, try each registered basename to + find as executable. Returns found dir/basename in real_filename. + Returns true when found. */ + bool check_path_access (path_conv& real_filename) + { + for (searchbufferlist::iterator dir = searchbuffers_.begin (); + dir != searchbuffers_.end (); + ++dir) + for (basenamelist::iterator name = basenames_.begin (); + name != basenames_.end (); + ++name) + { + /* complete the filename path to search for */ + memcpy (dir->buffer () + dir->stringlength (), name->string (), name->stringlength () + 1); + debug_printf ("trying %s", dir->buffer ()); + real_filename.check (dir->string (), PC_SYM_FOLLOW | PC_POSIX); + if (real_filename.exists () && !real_filename.isdir ()) + { + debug_printf (" found %s", dir->buffer ()); + return true; + } + } + real_filename.error = ENOENT; + return !real_filename.error; + } +}; + +#endif /* __cplusplus */ diff --git a/winsup/cygwin/vstrlist.h b/winsup/cygwin/vstrlist.h new file mode 100644 index 0000000..ecbdc64 --- /dev/null +++ b/winsup/cygwin/vstrlist.h @@ -0,0 +1,253 @@ +/* vstrlist.h: vstrlist + + Copyright 2016 Red Hat, Inc. + +This file is part of Cygwin. + +This software is a copyrighted work licensed under the terms of the +Cygwin license. Please consult the file "CYGWIN_LICENSE" for +details. */ + +#ifdef __cplusplus + +class vstrlist +{ +public: + class member + { + friend class vstrlist; + friend class iterator; + + member * prev_; + member * next_; + size_t bufferlength_; + size_t stringlength_; + char buffer_[1]; /* we always have space for a trailing zero */ + + /* no copy */ + member (member const &); + member & operator = (member const &); + + /* anchor */ + void * operator new (size_t class_size) + { + return cmalloc_abort (HEAP_STR, class_size); + } + + /* anchor */ + member () + : prev_ (this) + , next_ (this) + , bufferlength_ (0) + , stringlength_ (0) + , buffer_ () + {} + + /* entry: determine memory size from args */ + void * operator new (size_t class_size, char const * part0, va_list parts) + { + char const * part = part0; + va_list partsdup; + va_copy (partsdup, parts); + size_t bufferlength = 0; + while (part) + { + int partlen = va_arg (partsdup, int); + if (partlen < 0) + partlen = strlen (part); + bufferlength += partlen; + part = va_arg (partsdup, char const *); + } + va_end (partsdup); + + return cmalloc_abort (HEAP_STR, class_size + bufferlength); + } + + /* entry: instantly insert into list */ + member (member * before, char const * part0, va_list parts) + : prev_ (before->prev_) + , next_ (before) + , bufferlength_ (0) + , stringlength_ () + , buffer_ () + { + prev_->next_ = this; + next_->prev_ = this; + + char * dest = buffer_; + char const * part = part0; + va_list partsdup; + va_copy (partsdup, parts); + while (part) + { + int partlen = va_arg (partsdup, int); + if (partlen < 0) + { + char * old = dest; + dest = stpcpy (old, part); + partlen = dest - old; + } + else + dest = stpncpy (dest, part, partlen); + bufferlength_ += partlen; + part = va_arg (partsdup, const char *); + } + va_end (partsdup); + *dest = (char)0; + stringlength_ = dest - buffer_; + } + + void operator delete (void * p) + { + cfree (p); + } + + /* remove entry from list */ + ~member () + { + member * next = next_; + member * prev = prev_; + next->prev_ = prev; + prev->next_ = next; + prev_ = NULL; + next_ = NULL; + } + + public: + member const * next () const { return next_; } + member * next () { return next_; } + member const * prev () const { return next_; } + member * prev () { return next_; } + + /* always is a readonly string */ + char const * string () const { return buffer_; } + size_t stringlength () const { return stringlength_; } + + /* give write access to the buffer when writeable */ + char * buffer () { return buffer_; } + size_t bufferlength () { return bufferlength_; } + }; + + class iterator + { + friend class vstrlist; + + member * current_; + + iterator (); + + iterator (member * current) + : current_ (current) + {} + + public: + iterator (iterator const & rhs) + : current_ (rhs.current_) + {} + + iterator & operator = (iterator const & rhs) + { + current_ = rhs.current_; + return *this; + } + + iterator & operator ++ () + { + current_ = current_->next (); + return *this; + } + + iterator operator ++ (int) + { + iterator ret (*this); + current_ = current_->next (); + return ret; + } + + iterator & operator -- () + { + current_ = current_->prev (); + return *this; + } + + iterator operator -- (int) + { + iterator ret (*this); + current_ = current_->prev (); + return ret; + } + + bool operator == (iterator const & rhs) const + { + return current_ == rhs.current_; + } + + bool operator != (iterator const & rhs) const + { + return current_ != rhs.current_; + } + + member const & operator * () const { return *current_; } + member & operator * () { return *current_; } + member const * operator -> () const { return current_; } + member * operator -> () { return current_; } + + void remove () + { + member * old = current_; + ++ *this; + delete old; + } + }; + +private: + member * anchor_; + + /* no copy */ + vstrlist (vstrlist const &); + vstrlist & operator = (vstrlist const &); + +public: + iterator begin () { return iterator (anchor_->next ()); } + iterator end () { return iterator (anchor_ ); } + iterator rbegin () { return iterator (anchor_->prev ()); } + iterator rend () { return iterator (anchor_ ); } + + vstrlist () + : anchor_ (new member ()) + {} + + ~vstrlist () + { + for (iterator it = begin (); it != end (); it.remove ()); + delete anchor_; + } + + void swap (vstrlist & that) + { + member * old = anchor_; + anchor_ = that.anchor_; + that.anchor_ = old; + } + + member * appendv (char const * part0, va_list parts) + { + return new (part0, parts) member (anchor_, part0, parts); + } + + member * appendv (char const * part0, ...) + { + va_list parts; + va_start (parts, part0); + member * ret = appendv (part0, parts); + va_end (parts); + return ret; + } + + member * append (char const * part) + { + return appendv (part, strlen(part)); + } +}; + +#endif /* __cplusplus */ -- 2.8.3