This is the mail archive of the
cygwin-developers
mailing list for the Cygwin project.
Re: About the dll search algorithm of dlopen
- From: Michael Haubenwallner <michael dot haubenwallner at ssi-schaefer dot com>
- To: cygwin-developers at cygwin dot com
- Date: Fri, 19 Aug 2016 18:39:09 +0200
- Subject: Re: About the dll search algorithm of dlopen
- Authentication-results: sourceware.org; auth=none
- References: <574E835E.7090109@ssi-schaefer.com> <20160601110947.GE11431@calimero.vinschen.de> <574EF07B.1060806@ssi-schaefer.com> <20160601201748.GI11431@calimero.vinschen.de> <57B735D5.4070401@ssi-schaefer.com>
(and now with patches attached)
On 08/19/2016 06:37 PM, Michael Haubenwallner wrote:
> On 06/01/2016 10:17 PM, Corinna Vinschen wrote:
>> On Jun 1 16:26, Michael Haubenwallner wrote:
>>> On 06/01/2016 01:09 PM, Corinna Vinschen wrote:
>>>> On Jun 1 08:40, Michael Haubenwallner wrote:
>>>>> Hi,
>>>>>
>>>>> two issues with dlopen here (I'm about to prepare patches):
>>>>>
>>>>> *) The algorithm to combine dll file name variants with the search path
>>>>> entries needs to be reordered, as in:
>>>>> - for each dll file name variant:
>>>>> - for each search path:
>>>>> + for each search path entry:
>>>>> + for each dll file name variant:
>>>>> check if useable
>
>>>> However, if you can speed up the search process ignore the
>>>> question...
>
> Not sure if there's a speedup actually, ...
>
>>>
>>> This also depends on whether find_exec really is necessary here.
>>
>> Not as such necessary, it's just the function used to search in a
>> search path. If you want to change that you have to rewrite the
>> same logic again, just reversed.
>>
>> One way around YA code duplication could be some kind of path iterator
>> class which could be used from find_exec as well as from
>> get_full_path_of_dll.
>
> ... but:
> 0001.patch is a draft for some new cygwin::pathfinder class, with
> 0002.patch adding the executable's directory as searchpath, and
> 0003.patch to search the PATH environment as well.
>
> Thoughts?
> Any idea about different template nesting to be useful somewhere else?
>
>>>>> *) The directory of the current main executable should be searched
>>>>> after LD_LIBRARY_PATH and before /usr/bin:/usr/lib.
>>>>> And PATH should be searched before /usr/bin:/usr/lib as well.
>>>>
>>>> Checking the executable path and $PATH are Windows concepts. dlopen
>>>> doesn't do that on POSIX systems and we're not doing that either.
>>>
>>> Agreed, but POSIX also does have the concept of embedded RUNPATH,
>>> which is completely missing in Cygwin as far as I can see.
>>
>> RPATH and RUNPATH are ELF dynamic loader features, not supported by
>> PE/COFF.
>
> In any case, to me it does feel quite important to have the (almost) same
> dll search algorithm with dlopen() as with CreateProcess().
>
>>> However, when dlopen is about to search some path list, I can imagine to
>>> search the list of already loaded dlls first as well, but I'd prefer to
>>> leave this up to LoadLibrary...
>>
>> This problem would only occur if dlopen is not called with a path. If
>> the given pathname is a plain filename, we could simply add a call to
>> GetModuleHandle and if it succeeds, return the handle (after checking
>> for RTLD_NODELETE).
>
> But indeed this might lower the need for same search algorithms...
>
>>> Side note:
>>> We also use some cl.exe/link.exe wrapper that supports LD_PRELOAD,
>>> LD_LIBRARY_PATH, embedded RUNPATH, as well as lazy loading for both
>>> LoadLibrary and CreateProcess: https://github.com/haubi/parity
>>> Basically I'm wondering why Cygwin doesn't provide that (yet?)...
>>
>> We discussed implementing our own dynamic loader once, but gave up due
>> to workload. Parity is LGPLed and thus can't be included into Cygwin
>> itself.
>
> 've mentioned Parity more as proof-of-concept than for inclusion,
> and accepting workload as reasoning.
>
> Let's see if I can live without the ELF dynloader features.
>
> Thanks!
> /haubi/
>
>From fd7ed0d5cf2f255792e368bba3bc91c834d44896 Mon Sep 17 00:00:00 2001
From: Michael Haubenwallner <michael.haubenwallner@ssi-schaefer.com>
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 template, which
introduces and uses new samlist<vstring> templates.
---
winsup/cygwin/cygheap_malloc.h | 15 ++
winsup/cygwin/dlfcn.cc | 71 +++---
winsup/cygwin/pathfinder.h | 116 ++++++++++
winsup/cygwin/samlist.h | 253 +++++++++++++++++++++
winsup/cygwin/vstring.h | 503 +++++++++++++++++++++++++++++++++++++++++
5 files changed, 918 insertions(+), 40 deletions(-)
create mode 100644 winsup/cygwin/pathfinder.h
create mode 100644 winsup/cygwin/samlist.h
create mode 100644 winsup/cygwin/vstring.h
diff --git a/winsup/cygwin/cygheap_malloc.h b/winsup/cygwin/cygheap_malloc.h
index 74f0bb6..dfed929 100644
--- a/winsup/cygwin/cygheap_malloc.h
+++ b/winsup/cygwin/cygheap_malloc.h
@@ -52,4 +52,19 @@ char *__reg1 cstrdup1 (const char *);
void __reg2 cfree_and_set (char *&, char * = NULL);
}
+#ifdef __cplusplus
+
+namespace cygwin {
+
+ template<cygheap_types HeapType>
+ struct allocator
+ {
+ static void * alloc (size_t s) { return cmalloc_abort (HeapType, s); }
+ static void free (void *p) { cfree (p); }
+ };
+
+}
+
+#endif
+
#endif /*_CYGHEAP_MALLOC_H*/
diff --git a/winsup/cygwin/dlfcn.cc b/winsup/cygwin/dlfcn.cc
index 255a6d5..e2093ab 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 ();
+ cygwin::samlist<cygwin::vstring> 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.append (cygwin::vstring::constructor_args (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.append (cygwin::vstring::constructor_args ("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.append (cygwin::vstring::constructor_args (basename, baselen, ext, extlen, NULL));
+
+ cygwin::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..6845903
--- /dev/null
+++ b/winsup/cygwin/pathfinder.h
@@ -0,0 +1,116 @@
+/* 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 "samlist.h"
+#include "vstring.h"
+
+#ifdef __cplusplus
+
+namespace cygwin {
+
+class pathfinder
+{
+public:
+ typedef cygwin::samlist<cygwin::vstring> basenamelist;
+
+private:
+ pathfinder ();
+ pathfinder (pathfinder const &);
+ pathfinder & operator = (pathfinder const &);
+
+ basenamelist basenames_;
+ size_t basenames_maxlen_;
+
+ typedef cygwin::samlist<cygwin::vbuffer> searchbufferlist;
+ searchbufferlist searchbuffers_;
+
+public:
+ ~pathfinder () {}
+
+ /* We need the basenames to search for first, to allow for optimized
+ memory allocation of each searchpath + basename combination. */
+ pathfinder (cygwin::samlist<cygwin::vstring> & 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))
+ searchbuffers_.append (cygwin::vbuffer::constructor_args (dir, dirlen - 4, "/bin", 4, "/", 1 + basenames_maxlen_, NULL));
+// searchbuffers_.appendv (dir, dirlen - 4, "/bin", 4, "/", 1 + basenames_maxlen_, NULL);
+
+ /* prealloc buffer in searchdir for any basename we will search for */
+ searchbuffers_.append (cygwin::vbuffer::constructor_args (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 (cygwin::samlist<cygwin::vstring>::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/samlist.h b/winsup/cygwin/samlist.h
new file mode 100644
index 0000000..aec30ae
--- /dev/null
+++ b/winsup/cygwin/samlist.h
@@ -0,0 +1,253 @@
+/* samlist.h: cygwin::samlist
+
+ 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
+
+namespace cygwin
+{
+ /* basic member for SinglyAllocatedMembers list */
+ class samlist_member
+ {
+ samlist_member * prev_;
+ samlist_member * next_;
+
+ /* no copy */
+ samlist_member (samlist_member const &);
+ samlist_member & operator = (samlist_member const &);
+
+ public:
+ samlist_member ()
+ : prev_ (this)
+ , next_ (this)
+ {}
+
+ class constructor_args
+ {
+ friend class samlist_member;
+
+ template<typename MemberType>
+ friend class samlist;
+
+ samlist_member * before;
+
+ public:
+ constructor_args () /* list anchor */
+ : before (NULL)
+ {}
+
+ constructor_args (samlist_member * b) /* list anchor */
+ : before (b)
+ {}
+ };
+
+ samlist_member (constructor_args & ca)
+ {
+ if (!ca.before) /* default constructor */
+ {
+ /* anchor */
+ prev_ = this;
+ next_ = this;
+ return;
+ }
+ /* instantly insert into list */
+ prev_ = ca.before->prev_;
+ next_ = ca.before;
+ prev_->next_ = this;
+ next_->prev_ = this;
+ }
+
+ ~samlist_member ()
+ {
+ /* remove from list */
+ samlist_member * next = next_;
+ samlist_member * prev = prev_;
+ next->prev_ = prev;
+ prev->next_ = next;
+ prev_ = NULL;
+ next_ = NULL;
+ }
+
+ samlist_member * next ()
+ {
+ return next_;
+ }
+
+ samlist_member * prev ()
+ {
+ return next_;
+ }
+ };
+
+ /* Double-linked list with SinglyAllocatedMember storage, that is,
+ double-linkage pointers are allocated alongside the member data. */
+ template<typename MemberType>
+ class samlist
+ {
+ public:
+ struct member_type
+ : public samlist_member
+ , public MemberType
+ {
+ struct constructor_args
+ : public samlist_member::constructor_args
+ , public MemberType::constructor_args
+ {
+ constructor_args (samlist_member::constructor_args lca,
+ typename MemberType::constructor_args mca)
+ : samlist_member::constructor_args (lca)
+ , MemberType::constructor_args (mca)
+ {}
+ };
+
+ member_type (constructor_args & ca)
+ : samlist_member (ca)
+ , MemberType (ca)
+ {}
+
+ void * operator new (size_t classSize, constructor_args & ca)
+ {
+ return MemberType::operator new (classSize, ca);
+ }
+ };
+
+ class iterator
+ {
+ samlist_member * current_;
+
+ iterator ();
+
+ friend class samlist;
+ iterator (samlist_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 !(*this == rhs);
+ }
+
+ member_type const & operator * () const { return *ptr (); }
+ member_type & operator * () { return *ptr (); }
+ member_type const * operator -> () const { return ptr (); }
+ member_type * operator -> () { return ptr (); }
+
+ operator member_type const * () const { return ptr (); }
+ operator member_type * () { return ptr (); }
+
+ member_type const * ptr () const { return static_cast<member_type *>(current_); }
+ member_type * ptr () { return static_cast<member_type *>(current_); }
+
+ void remove ()
+ {
+ member_type * old = ptr ();
+ ++ *this;
+ delete old;
+ }
+ };
+
+ private:
+ samlist_member * anchor_;
+
+ /* no copy */
+ samlist (samlist const &);
+ samlist & operator = (samlist const &);
+
+ public:
+ iterator begin () { return iterator (anchor_->next ()); }
+ iterator end () { return iterator (anchor_ ); }
+ iterator rbegin () { return iterator (anchor_->prev ()); }
+ iterator rend () { return iterator (anchor_ ); }
+
+ samlist ()
+ : anchor_ (new samlist_member ())
+ {}
+
+ ~samlist ()
+ {
+ for (iterator it = begin (); it != end (); it.remove ());
+ delete anchor_;
+ }
+
+ void swap (samlist & that)
+ {
+ samlist_member * old = anchor_;
+ anchor_ = that.anchor_;
+ that.anchor_ = old;
+ }
+
+ member_type * append (typename MemberType::constructor_args ca)
+ {
+ typename member_type::constructor_args mca (samlist_member::constructor_args (anchor_), ca);
+ return new (mca) member_type (mca);
+ }
+
+ template<typename FirstType>
+ member_type * appendv (FirstType & firstArg, va_list const & moreArgs)
+ {
+ typename member_type::constructor_args ca (firstArg, moreArgs);
+ return append (ca);
+ }
+
+ template<typename FirstType>
+ member_type * appendv(FirstType & firstArg, ...)
+ {
+ va_list moreArgs;
+ va_start (moreArgs, firstArg);
+ member_type * ret = appendv (firstArg, moreArgs);
+ va_end (moreArgs);
+ return ret;
+ }
+ };
+
+}
+
+#endif /* __cplusplus */
diff --git a/winsup/cygwin/vstring.h b/winsup/cygwin/vstring.h
new file mode 100644
index 0000000..0658699
--- /dev/null
+++ b/winsup/cygwin/vstring.h
@@ -0,0 +1,503 @@
+/* vstring.h: cygwin::vstring, cygwin::vbuffe
+
+ 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 <assert.h>
+#include "cygheap_malloc.h"
+
+#ifdef __cplusplus
+
+namespace cygwin {
+
+ struct constructible {
+ struct constructor_args {};
+ constructible (constructor_args &) {}
+ };
+
+ namespace string {
+
+ template<typename CharType>
+ struct char_traits
+ {
+ typedef CharType char_type;
+ typedef size_t size_type;
+
+ /* see strlen */
+ static size_type length (char_type const *);
+
+ /* see stpcpy */
+ static char_type * copy (char_type *, char_type const *);
+
+ /* see stpncpy */
+ static char_type * copy (char_type *, char_type const *, size_type);
+ };
+
+ template<>
+ struct char_traits<char>
+ {
+ typedef char char_type;
+ typedef size_t size_type;
+
+ static size_type length(char_type const * s)
+ {
+ return strlen (s);
+ }
+ static char_type * copy(char_type * d, char_type const * s)
+ {
+ return stpcpy (d, s);
+ }
+ static char_type * copy(char_type * d, char_type const * s, size_type n)
+ {
+ return stpncpy (d, s, n);
+ }
+ };
+
+ template<>
+ struct char_traits<wchar_t>
+ {
+ typedef wchar_t char_type;
+ typedef size_t size_type;
+
+ static size_type length(char_type const *s)
+ {
+ return wcslen (s);
+ }
+ static char_type * copy(char_type *d, char_type const *s)
+ {
+ return wcpcpy (d, s);
+ }
+ static char_type * copy(char_type *d, char_type const *s, size_type n)
+ {
+ return wcpncpy (d, s, n);
+ }
+ };
+
+ namespace storage {
+
+ template<typename CharTraits, typename AllocatorType>
+ class varying_length /* must be last data member wherever used */
+ {
+ protected:
+ typedef CharTraits char_traits;
+ typedef AllocatorType allocator_type;
+ typedef typename CharTraits::char_type char_type;
+ typedef typename CharTraits::size_type size_type;
+
+ private:
+ union {
+ struct {
+ size_type bufferlength_; /* initialized by operator new */
+ size_type stringlength_; /* abused to check for operator new */
+ /* must be last data member even in derived class */
+ char_type buffer_[1]; /* we always have space for trailing zero */
+ };
+ double alignment_;
+ };
+
+ /* variable length disallows placement new */
+ void * operator new (size_t classSize, void * p);
+
+ protected:
+ varying_length ()
+ : stringlength_ ()
+ , buffer_ ()
+ {}
+
+ struct constructor_args
+ {
+ size_type bufferlength;
+
+ constructor_args ()
+ : bufferlength (0)
+ {}
+
+ constructor_args (size_type l)
+ : bufferlength (l)
+ {}
+
+ void set (size_type l)
+ {
+ bufferlength = l;
+ }
+ };
+
+ varying_length (constructor_args & ca)
+ {
+ /* check if our operator new was used as expected */
+ assert (bufferlength_ == ca.bufferlength);
+ assert (stringlength_ == (size_type) this);
+ stringlength_ = (size_type) 0;
+ buffer_[0] = (char_type) 0;
+ }
+
+ size_type bufferlength () const { return bufferlength_; }
+ size_type stringlength () const { return stringlength_; }
+ char_type const * buffer () const { return buffer_; }
+ char_type * buffer () { return buffer_; }
+
+ void stringlength (size_type stringLength)
+ {
+ assert (stringLength <= bufferlength_);
+ stringlength_ = stringLength;
+ }
+
+ void * operator new (size_t classSize, constructor_args & ca)
+ {
+ void *ret = allocator_type::alloc (classSize + ca.bufferlength);
+ /* we are last data member, aligned to the end of real struct:
+ find self to initialize bufferlength_ */
+ union { void *v; char *b; varying_length *p; } self;
+ self.v = ret;
+ self.b += classSize - sizeof (varying_length);
+ /* tell constructor about our allocation */
+ self.p->bufferlength_ = ca.bufferlength;
+ self.p->stringlength_ = (size_type) self.p;
+ return ret;
+ }
+
+ void operator delete (void * p)
+ {
+ allocator_type::free (p);
+ }
+ };
+
+ }
+
+ namespace access {
+
+ template<typename AllocatorType>
+ class readonly
+ : protected AllocatorType /* hide AllocatorType::buffer () */
+ {
+ public:
+ typedef AllocatorType storage_type;
+ using typename storage_type::char_type;
+ using typename storage_type::size_type;
+
+ using typename AllocatorType::constructor_args;
+
+ readonly (constructor_args & c)
+ : AllocatorType (c)
+ {}
+
+ char_type const * string () const { return AllocatorType::buffer (); }
+ size_type stringlength () const { return AllocatorType::stringlength (); }
+ };
+
+ template<typename AllocatorType>
+ class writeable
+ : public readonly<AllocatorType>
+ {
+ public:
+ using typename readonly<AllocatorType>::storage_type;
+ using typename readonly<AllocatorType>::char_type;
+ using typename readonly<AllocatorType>::size_type;
+
+ using typename readonly<AllocatorType>::constructor_args;
+
+ writeable (constructor_args & c)
+ : readonly<AllocatorType> (c)
+ {}
+
+ char_type * buffer () { return storage_type::buffer (); }
+ size_type bufferlength () { return storage_type::bufferlength (); }
+ };
+
+ }
+
+ /* Extend some ConstructibleBaseType by one string with length unknown before
+ construction.
+ The ConstructibleBaseType is required to provide the contructor_args type
+ to support polymorphic constructor arguments;
+ Provides
+ The new operator needs to get the (varying) constructor
+ arguments for determining required memory size, while the constructor
+ itself assumes enough memory being allocated to copy the varying
+ string arguments. */
+ template<typename AccessType, typename ConstructibleBaseType>
+ class varying_extension
+ : public ConstructibleBaseType
+ , public AccessType /* must be last data member */
+ {
+ public:
+ using typename AccessType::storage_type;
+
+ using typename AccessType::char_traits;
+ using typename AccessType::char_type;
+ using typename AccessType::size_type;
+
+ struct constructor_args
+ : public ConstructibleBaseType::constructor_args
+ , public AccessType::constructor_args
+ {
+ char_type const * part0;
+ va_list parts;
+
+ constructor_args ()
+ : ConstructibleBaseType::constructor_args ()
+ , AccessType::constructor_args ()
+ , part0 (NULL)
+ , parts ()
+ {}
+
+ constructor_args (char_type const * p0, va_list const & pn)
+ : ConstructibleBaseType::constructor_args ()
+ , AccessType::constructor_args ()
+ , part0 (p0)
+ , parts ()
+ {
+ if (part0)
+ va_copy (parts, pn);
+ }
+
+ constructor_args (char_type const * p0, ...)
+ : ConstructibleBaseType::constructor_args ()
+ , AccessType::constructor_args ()
+ , part0 (p0)
+ , parts ()
+ {
+ if (part0)
+ va_start (parts, p0);
+ }
+
+ void set (char_type const * p0, va_list const & pn)
+ {
+ if (part0)
+ va_end (parts);
+ part0 = p0;
+ if (part0)
+ va_copy (parts, pn);
+ }
+
+ void set (char_type const * p0, ...)
+ {
+ if (part0)
+ va_end (parts);
+ part0 = p0;
+ if (part0)
+ va_start (parts, p0);
+ }
+
+ ~constructor_args ()
+ {
+ if (part0)
+ va_end (parts);
+ }
+ };
+
+
+ void operator delete (void * p)
+ {
+ AccessType::operator delete (p);
+ }
+
+ void * operator new (size_t classSize)
+ {
+ constructor_args ca;
+ return AccessType::operator new (classSize, ca);
+ }
+
+ void * operator new (size_t classSize, constructor_args & ca)
+ {
+ char_type const * part0 = ca.part0;
+ va_list parts;
+ va_copy (parts, ca.parts);
+ ca.bufferlength = 0;
+ while (part0)
+ {
+ int part0len = va_arg (parts, int);
+ if (part0len < 0)
+ part0len = char_traits::length (part0);
+ ca.bufferlength += part0len;
+ part0 = va_arg (parts, const char_type *);
+ }
+ va_end (parts);
+
+ /* allocate member- and string-buffer at once */
+ return AccessType::operator new (classSize, ca);
+ }
+
+ varying_extension (constructor_args & ca)
+ : ConstructibleBaseType (ca)
+ , AccessType (ca)
+ {
+ char_type * dest = storage_type::buffer ();
+ size_type bufferLength = 0;
+ char_type const * part0 = ca.part0;
+ va_list parts;
+ va_copy (parts, ca.parts);
+ while (part0)
+ {
+ int part0len = va_arg (parts, int);
+ if (part0len < 0)
+ {
+ char_type * old = dest;
+ dest = char_traits::copy (old, part0);
+ part0len = dest - old;
+ }
+ else
+ dest = char_traits::copy (dest, part0, part0len);
+ bufferLength += part0len;
+ part0 = va_arg (parts, const char_type *);
+ }
+ va_end (parts);
+ *dest = (char_type)0;
+ storage_type::stringlength (dest - storage_type::buffer ());
+ assert (bufferLength == storage_type::bufferlength ());
+ }
+
+ template<typename FinalType>
+ static FinalType * create (typename FinalType::constructor_args & ca)
+ {
+ return new (ca) FinalType (ca);
+ }
+
+ template<typename FinalType>
+ static FinalType * create (typename FinalType::constructor_args & c, const char_type * part0, va_list const & parts)
+ {
+ c.set (part0, parts);
+ return create (c);
+ }
+
+ template<typename FinalType>
+ static FinalType * create (typename FinalType::constructor_args & c, const char_type * part0, ...)
+ {
+ va_list parts;
+ va_start (parts, part0);
+ c.set (part0, parts);
+ va_end (parts);
+ return create (c);
+ }
+
+ template<typename FinalType>
+ static FinalType * create (const char_type * part0, va_list const & parts)
+ {
+ typename FinalType::constructor_args c;
+ c.set (part0, parts);
+ return create (c);
+ }
+
+ template<typename FinalType>
+ static FinalType * create (const char_type * part0, ...)
+ {
+ typename FinalType::constructor_args c;
+ va_list parts;
+ va_start (parts, part0);
+ c.set (part0, parts);
+ va_end (parts);
+ return create (c);
+ }
+ };
+ }
+
+ template<typename CharType, typename ConstructibleBaseType, typename Allocator>
+ struct basic_vstring_extension
+ : public string::varying_extension
+ < string::access::readonly
+ < string::storage::varying_length
+ < string::char_traits<CharType>
+ , Allocator
+ > >
+ , ConstructibleBaseType
+ >
+ {
+ using typename string::varying_extension
+ < string::access::readonly
+ < string::storage::varying_length
+ < string::char_traits<CharType>
+ , Allocator
+ > >
+ , ConstructibleBaseType
+ >::constructor_args;
+ basic_vstring_extension (constructor_args & ca)
+ : string::varying_extension
+ < string::access::readonly
+ < string::storage::varying_length
+ < string::char_traits<CharType>
+ , Allocator
+ > >
+ , ConstructibleBaseType
+ > (ca)
+ {}
+ };
+
+ template<typename CharType, typename ConstructibleBaseType, typename Allocator>
+ struct basic_vbuffer_extension
+ : public string::varying_extension
+ < string::access::writeable
+ < string::storage::varying_length
+ < string::char_traits<CharType>
+ , Allocator
+ > >
+ , ConstructibleBaseType
+ >
+ {
+ using typename string::varying_extension
+ < string::access::writeable
+ < string::storage::varying_length
+ < string::char_traits<CharType>
+ , Allocator
+ > >
+ , ConstructibleBaseType
+ >::constructor_args;
+
+ basic_vbuffer_extension (constructor_args & ca)
+ : string::varying_extension
+ < string::access::writeable
+ < string::storage::varying_length
+ < string::char_traits<CharType>
+ , Allocator
+ > >
+ , ConstructibleBaseType
+ > (ca)
+ {}
+ };
+
+ template<typename ConstructibleBaseType, typename Allocator>
+ struct vstring_extension
+ : public basic_vstring_extension<char, ConstructibleBaseType, Allocator>
+ {
+ using typename basic_vstring_extension<char, ConstructibleBaseType, Allocator>::constructor_args;
+ vstring_extension (constructor_args & ca)
+ : basic_vstring_extension<char, ConstructibleBaseType, Allocator> (ca)
+ {}
+ };
+
+ template<typename ConstructibleBaseType, typename Allocator>
+ struct wvstring_extension
+ : public basic_vstring_extension<wchar_t, ConstructibleBaseType, Allocator>
+ {
+ using typename basic_vstring_extension<wchar_t, ConstructibleBaseType, Allocator>::constructor_args;
+ };
+
+ template<typename ConstructibleBaseType, typename Allocator>
+ struct vbuffer_extension
+ : public basic_vbuffer_extension<char, ConstructibleBaseType, Allocator>
+ {
+ using typename basic_vbuffer_extension<char, ConstructibleBaseType, Allocator>::constructor_args;
+ vbuffer_extension (constructor_args & ca)
+ : basic_vbuffer_extension<char, ConstructibleBaseType, Allocator> (ca)
+ {}
+ };
+
+ template<typename ConstructibleBaseType, typename Allocator>
+ struct wvbuffer_extension
+ : public basic_vbuffer_extension<wchar_t, ConstructibleBaseType, Allocator>
+ {
+ using typename basic_vbuffer_extension<wchar_t, ConstructibleBaseType, Allocator>::constructor_args;
+ };
+
+ typedef vstring_extension<constructible, allocator<HEAP_STR> > vstring;
+ typedef vbuffer_extension<constructible, allocator<HEAP_STR> > vbuffer;
+
+ typedef wvstring_extension<constructible, allocator<HEAP_STR> > wvstring;
+ typedef wvbuffer_extension<constructible, allocator<HEAP_STR> > wvbuffer;
+}
+
+#endif /* __cplusplus */
--
2.8.3
>From 6e2739deccb06a4d739d2a41fa32355f21c151a1 Mon Sep 17 00:00:00 2001
From: Michael Haubenwallner <michael.haubenwallner@ssi-schaefer.com>
Date: Tue, 19 Jul 2016 14:56:48 +0200
Subject: [PATCH 2/3] dlopen: search main executable's directory
---
winsup/cygwin/dlfcn.cc | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/winsup/cygwin/dlfcn.cc b/winsup/cygwin/dlfcn.cc
index e2093ab..4ea9984 100644
--- a/winsup/cygwin/dlfcn.cc
+++ b/winsup/cygwin/dlfcn.cc
@@ -80,6 +80,19 @@ get_full_path_of_dll (const char* str, path_conv &real_filename)
not use the LD_LIBRARY_PATH environment variable. */
finder.add_envsearchpath ("LD_LIBRARY_PATH");
+ /* Unless we have a special cygwin loader, there is no such thing like
+ DT_RUNPATH on Windows we can use to search for dlls, except for the
+ directory of the main executable. */
+ tmp_pathbuf tp;
+ PWCHAR exewname = tp.w_get ();
+ GetModuleFileNameW (NULL, exewname, NT_MAX_PATH);
+ char * exedir = tp.c_get ();
+ *exedir = '\0';
+ cygwin_conv_path (CCP_WIN_W_TO_POSIX, exewname, exedir, NT_MAX_PATH);
+ char * lastsep = strrchr (exedir, '/');
+ if (lastsep)
+ finder.add_searchdir (exedir, lastsep - exedir);
+
/* Finally we better have some fallback. */
finder.add_searchdir ("/usr/lib", -1);
}
--
2.8.3
>From efe31198bf5747253a9c0d588c354a6b86234525 Mon Sep 17 00:00:00 2001
From: Michael Haubenwallner <michael.haubenwallner@ssi-schaefer.com>
Date: Tue, 19 Jul 2016 14:57:10 +0200
Subject: [PATCH 3/3] dlopen: search PATH environment variable
---
winsup/cygwin/dlfcn.cc | 3 +++
1 file changed, 3 insertions(+)
diff --git a/winsup/cygwin/dlfcn.cc b/winsup/cygwin/dlfcn.cc
index 4ea9984..302869d 100644
--- a/winsup/cygwin/dlfcn.cc
+++ b/winsup/cygwin/dlfcn.cc
@@ -93,6 +93,9 @@ get_full_path_of_dll (const char* str, path_conv &real_filename)
if (lastsep)
finder.add_searchdir (exedir, lastsep - exedir);
+ /* Windows uses the PATH environment variable to search for dlls. */
+ finder.add_envsearchpath ("PATH");
+
/* Finally we better have some fallback. */
finder.add_searchdir ("/usr/lib", -1);
}
--
2.8.3