From: Keith M. <no...@so...> - 2014-11-17 19:12:44
|
This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "Repository: mingw-org-wsl". The branch, legacy has been updated via 417de1ac84aedc9749f32211c83d6c3c21d69cc9 (commit) via 739d533acd49940efaf062a6ddbfb4a83526aed7 (commit) via e974c5e23a4213c6c9e22c6c812501cf35e30cd9 (commit) from 9a09e9224a0229440fd7b4fb5210941e80ad7a1d (commit) Those revisions listed above that are new to this repository have not appeared on any other notification email; so we list those revisions in full, below. - Log ----------------------------------------------------------------- https://sf.net/p/mingw/mingw-org-wsl/ci/417de1ac84aedc9749f32211c83d6c3c21d69cc9/ commit 417de1ac84aedc9749f32211c83d6c3c21d69cc9 Author: Keith Marshall <kei...@us...> Date: Mon Nov 17 19:10:03 2014 +0000 Implement more robust _get_output_format fallback handling. diff --git a/mingwrt/ChangeLog b/mingwrt/ChangeLog index 03d165c..372c0ea 100644 --- a/mingwrt/ChangeLog +++ b/mingwrt/ChangeLog @@ -1,3 +1,50 @@ +2014-11-17 Keith Marshall <kei...@us...> + + Implement more robust _get_output_format fallback handling. + + * mingwex/ofmt.c: New file; it implements... + (__mingw_output_format_flag): ...backing store, used by... + (__mingw_get_output_format, __mingw_set_output_format): ...these + MSVCRT.DLL version agnostic wrappers/emulators for each of these... + (_get_output_format, _set_output_format): ...MSVCR80.DLL and later + version specific Microsoft APIs, which are also supported by some + later versions of MSVCRT.DLL, (excluding WinXP and earlier). + (__mingw_get_output_format_fallback): Implement; it is aliased to... + (__mingw_set_output_format_fallback): ...this; together they provide + the emulation support for the preceding pair of functions, when they + are not available within the run-time platform MSVCRT.DLL + (__mingw_get_printf_count_output, __mingw_set_printf_count_output): + Implement them; they wrap, and if necessary emulate... + (_get_printf_count_output, _set_printf_count_output): ...these. + (__mingw_get_printf_count_output_fallback): Implement; aliased to... + (__mingw_set_printf_count_output_fallback): ...this; together they are + required to support emulation of the preceding pair of functions, when + they are not available within the run-time platform MSVCRT.DLL + + * include/stdio.h (_TWO_DIGIT_EXPONENT): Define it unconditionally. + (_THREE_DIGIT_EXPONENT): New manifest constant; define it orthogonally. + (__mingw_get_output_format, __mingw_set_output_format) + (__mingw_get_printf_count_output, __mingw_set_printf_count_output): + Unconditionally declare their prototypes, for subsequent inline use. + [_MSVCRT_VERSION__ < 0x800] (_get_output_format): Emulate it inline. + [_MSVCRT_VERSION__ < 0x800] (_set_output_format): Likewise. + [_MSVCRT_VERSION__ < 0x800] (_get_printf_count_output): Likewise. + [_MSVCRT_VERSION__ < 0x800] (_set_printf_count_output): Likewise. + (__USE_MINGW_PRINTF): New manifest constant; define it. It is a + counterpart to __USE_MINGW_ANSI_STDIO, which is guaranteed to always + represent a compilable token. + + * mingwex/stdio/pformat.c (__pformat_exponent_digits): Do not require + _get_output_format; consult __mingw_output_format_flag directly. + + * ofmt_stub.s: No longer required; delete it. + + * Makefile.in (ofmt_stub.$OBJEXT): Delete reference. + (PFORMAT_CFLAGS) [__MSVCRT_VERSION__ = 0x800]: Remove requirement. + (varofmt.$OBJEXT, crtofmt.$OBJEXT, getofmt.$OBJEXT, setofmt.$OBJEXT) + (crtnfmt.$OBJEXT, getnfmt.$OBJEXT, setnfmt.$OBJEXT): Add build rule; + all are compiled from ofmt.c + 2014-11-16 Keith Marshall <kei...@us...> Do some more essential build tree clean up. diff --git a/mingwrt/Makefile.in b/mingwrt/Makefile.in index 1e10ec5..9555cbc 100644 --- a/mingwrt/Makefile.in +++ b/mingwrt/Makefile.in @@ -258,19 +258,6 @@ all-libmsvcrt install-mingwrt-libs: $(foreach name,$(all_msvcrt),lib$(name).a) all-libcrtdll install-deprecated-mingwrt-libs: libcrtdll.a libcoldname.a all-deprecated-mingwrt all-deprecated-libmsvcrt: all-libcrtdll -# FIXME: We should aim to remove this interim work-around... -# -# MSVCRT.DLL, MSVCR70.DLL, and MSVCR71.DLL, (both regular and debug -# variants), lack the _get_output_format() function, which is required -# by the printf() module in libmingwex.a; add our own implementation. -# (Note that we also apply this for CRTDLL.DLL, via libcrtdll.a) -# -vpath ofmt_stub.s ${mingwrt_srcdir} -$(foreach ver,t 70 71,libmsvcr$(ver).a) libcrtdll.a \ -$(foreach ver,t 70 71,libmsvcr$(ver)d.a): ofmt_stub.$(OBJEXT) -# -# End of work-around - # For each import library to be built, we derive a tailored exports # definition file, from a common source. # @@ -437,21 +424,21 @@ libmingwex.a: $(addsuffix .$(OBJEXT), btowc fopen64 fprintf fseeko64 ftello64 \ lseek64 pformat printf snprintf snwprintf sprintf vfprintf vfscanf vfwscanf \ vprintf vscanf vsnprintf vsnwprintf vsprintf vsscanf vswscanf vwscanf) -# FIXME: We should adopt a semantic implementation similar to the -# dlsym( RTLD_DEFAULT, "_get_output_format" ) call in POSIX, to let -# us handle the following fall-back more effectively. +# pformat.$(OBJEXT) needs an explicit build rule, since we need to +# specify an additional header file path. # -# pformat.$(OBJEXT) needs an explicit build rule; we always build it -# assuming that __MSVCRT_VERSION__ >= 0x0800, (and thus assuming that -# the Microsoft runtime provides the _get_output_format() function); -# we then rely on ofmt_stub.s, (in the top mingwrt source directory), -# to provide a fall-back implementation, so maintaining forward -# compatibility for earlier versions of MSVCRT.DLL -# -PFORMAT_CFLAGS = -I ${mingwrt_srcdir}/mingwex/gdtoa -D__MSVCRT_VERSION__=0x0800 +PFORMAT_CFLAGS = -I ${mingwrt_srcdir}/mingwex/gdtoa pformat.$(OBJEXT): %.$(OBJEXT): %.c $(CC) -c $(ALL_CFLAGS) $(PFORMAT_CFLAGS) $< -o $@ +# To support Microsoft's DLL version specific exponent digits control, +# and "%n" format availability control APIs, in a DLL version agnostic +# manner, we also provide the following set of wrapper functions: +# +libmingwex.a: $(addsuffix fmt.$(OBJEXT),varo crto geto seto crtn getn setn) +$(addsuffix fmt.$(OBJEXT),varo crto geto seto crtn getn setn): %.$(OBJEXT): ofmt.c + $(CC) -c $(ALL_CFLAGS) -D__$*__ -fno-align-functions -o $@ $< + # Some additional miscellaneous functions, in libmingwex.a # #libmingwex.a: $(addsuffix .$(OBJEXT), glob membarrier) diff --git a/mingwrt/include/stdio.h b/mingwrt/include/stdio.h index 6ec40e2..84ab7b7 100644 --- a/mingwrt/include/stdio.h +++ b/mingwrt/include/stdio.h @@ -508,14 +508,80 @@ _CRTIMP int __cdecl __MINGW_NOTHROW _getmaxstdio (void); _CRTIMP int __cdecl __MINGW_NOTHROW _setmaxstdio (int); #endif +/* Microsoft introduced a capability in MSVCR80.DLL and later, to + * set the minimum number of digits to be displayed in a printf() + * floating point exponent; they retro-fitted this in MSVCRT.DLL, + * from Windows-Vista onwards, but we provide our own wrappers in + * libmingwex.a, which make it possible for us to emulate the API + * for any version of MSVCRT.DLL (including WinXP and earlier). + */ +#define _TWO_DIGIT_EXPONENT 1 + +/* While Microsoft define the preceding manifest constant, they + * appear to neglect to define its complement, (for restoration + * of their default exponent display format); for orthogonality, + * we will provide this regardless of Microsoft's negligence. + */ +#define _THREE_DIGIT_EXPONENT 0 + +unsigned int __cdecl __mingw_get_output_format (void); +unsigned int __cdecl __mingw_set_output_format (unsigned int); + +/* Also appearing for the first time in MSVCR80.DLL, and then also + * retro-fitted to MSVCRT.DLL from Windows-Vista onwards, was this + * pair of functions to control availability of "%n" formatting in + * the MSVCRT.DLL printf() family of functions, for which we also + * provide our own DLL version agnostic wrappers: + */ +int __cdecl __mingw_get_printf_count_output (void); +int __cdecl __mingw_set_printf_count_output (int); + #if __MSVCRT_VERSION__ >= 0x800 +/* + * When the user declares that MSVCR80.DLL features are supported, + * we simply expose the corresponding APIs... + */ _CRTIMP unsigned int __cdecl __MINGW_NOTHROW _get_output_format (void); _CRTIMP unsigned int __cdecl __MINGW_NOTHROW _set_output_format (unsigned int); -#define _TWO_DIGIT_EXPONENT 1 - _CRTIMP int __cdecl __MINGW_NOTHROW _get_printf_count_output (void); _CRTIMP int __cdecl __MINGW_NOTHROW _set_printf_count_output (int); + +#else +/* ...otherwise, we emulate the APIs, in a DLL version agnostic + * manner, using our own implementation wrappers. + */ +__CRT_ALIAS unsigned int __cdecl _get_output_format (void) +{ return __mingw_get_output_format (); } + +__CRT_ALIAS unsigned int __cdecl _set_output_format (unsigned int __style) +{ return __mingw_set_output_format (__style); } + +/* When using our own printf() implementation, "%n" format is ALWAYS + * supported, so we make this API a no-op, reporting it to be so; for + * the alternative case, when using MSVCRT.DLL's printf(), we delegate + * to our wrapper API implementation, which will invoke the API function + * calls within the DLL, if they are available, or persistently report + * the state of "%n" formatting as DISABLED if they are not. + */ +#if __USE_MINGW_ANSI_STDIO +/* + * Note that __USE_MINGW_ANSI_STDIO is not guaranteed to resolve to any + * symbol which will represent a compilable logic state; map it to this + * alternative which will, for the true state... + */ +# define __USE_MINGW_PRINTF 1 +#else +/* ...and for the false. + */ +# define __USE_MINGW_PRINTF 0 +#endif + +__CRT_ALIAS int __cdecl _get_printf_count_output (void) +{ return __USE_MINGW_PRINTF ? 1 : __mingw_get_printf_count_output (); } + +__CRT_ALIAS int __cdecl _set_printf_count_output (int __mode) +{ return __USE_MINGW_PRINTF ? 1 : __mingw_set_printf_count_output (__mode); } #endif #ifndef _NO_OLDNAMES diff --git a/mingwrt/mingwex/ofmt.c b/mingwrt/mingwex/ofmt.c new file mode 100644 index 0000000..5a8ccf3 --- /dev/null +++ b/mingwrt/mingwex/ofmt.c @@ -0,0 +1,241 @@ +/* + * ofmt.c + * + * Implement universally available replacement wrappers for each of + * Microsoft's _get_output_format()/_set_output_format(), and their + * _get_printf_count_output()/_set_printf_count_output() APIs. + * + * $Id$ + * + * Written by Keith Marshall <kei...@us...> + * Copyright (C) 2014, MinGW.org Project + * + * + * 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, this permission notice, and the following + * disclaimer 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 OF OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * + * This source file provides a generic implementation for each of the two + * functions, __mingw_get_output_format() and __mingw_set_output_format(), + * which serve as universally available fallbacks for each of Microsoft's + * _get_output_format() and _set_output_format() API functions, which were + * introduced in MSVCR80.DLL, and are unavailable in MSVCRT.DLL prior to + * the release accompanying Windows-Vista; compile it with: + * + * gcc -c -O2 -D__varofmt__ -o varofmt.o ofmt.c + * gcc -c -O2 -D__crtofmt__ -o crtofmt.o ofmt.c + * gcc -c -O2 -D__getofmt__ -o getofmt.o ofmt.c + * gcc -c -O2 -D__setofmt__ -o setofmt.o ofmt.c + * + * for each of these two implementation cases respectively, to provide the + * capability for any application which #includes stdio.h to invoke either + * _get_output_format() or _set_output_format() in total safety, regardless + * of whether the underlying platform MSVCRT.DLL supports this API or not; + * if the API is available, it will be invoked by the wrappers, otherwise + * it will be emulated within the wrappers themselves. + * + * Additionally provided are wrappers for the _get_printf_count_output() and + * _set_printf_count_output() functions, the respective invocations of which + * may be compiled with: + * + * gcc -c -O2 -D__crtnfmt__ -o crtnfmt.o ofmt.c + * gcc -c -O2 -D__getnfmt__ -o getnfmt.o ofmt.c + * gcc -c -O2 -D__setnfmt__ -o setnfmt.o ofmt.c + * + * to provide similar fallback handlers for the respective API functions; in + * the case of this pair of functions, the respective API will be invoked if + * available, otherwise the call will become a no-op, always returning zero. + * + * All of these functions are provided in libmingwex.a, where each of them + * serves as a wrapper for their corresponding Microsoft runtime functions; + * each may be called safely, even when running on a version of MS-Windows + * where the native MSVCRT.DLL implementation lacks the corresponding API + * function, in which case a suitable fallback implementation is provided; + * in cases where the native implementation IS available, each wrapper + * simply delegates to the MSVCRT.DLL implementation. + * + * Note that each of these wrapper functions depends upon the MinGW.org + * implementation of the POSIX dlsym() function, which is also provided + * in libmingwex.a, and prototyped in dlfcn.h + * + */ +#include <dlfcn.h> +#include <stdio.h> + +/* Storage for recorded output format state, when the MSVCRT functions + * aren't available; we need it to be public so that it may be shared by + * our two separately compiled API functions, but we keep it privately + * declared; users should NEVER access this, other than via our two + * accessor functions. + */ +extern unsigned int __mingw_output_format_flag; + +#if defined __varofmt__ +/* + * Here, we actually allocate the storage for recording the preferred + * formatting style; although POSIX might lead us to prefer an initial + * default of _TWO_DIGIT_EXPONENT, we choose to assign a default which + * is consistent with Microsoft's preference. + */ +unsigned int __mingw_output_format_flag = _THREE_DIGIT_EXPONENT; + +#elif defined __crtofmt__ +/* + * Here we implement the common part of the fallback API, retrieving + * the value stored in __mingw_output_format_flag. Note that this is + * sufficient for both _get_output_format() and _set_output_format(), + * with the set action being completed in the calling wrapper. + */ +extern unsigned int __mingw_get_output_format_fallback( void ); +unsigned int __mingw_get_output_format_fallback( void ) +{ + /* Our replacement function simply returns the current setting of + * the assigned formatting style... + */ + return __mingw_output_format_flag; +} +/* ...and, in the case of _set_output_format(), we simply map the + * requisite name to the common function implementation. + */ +extern unsigned int __mingw_set_output_format_fallback( unsigned int ) +__attribute__((__alias__("__mingw_get_output_format_fallback"))); + +#elif defined __crtnfmt__ +/* + * Here, we implement a generic fallback hook, suitable for use as the + * fallback for _get_printf_count_output()/_set_printf_count_output(). + */ +int __mingw_get_printf_count_output_fallback( void ) +__attribute__((__alias__("__mingw_set_printf_count_output_fallback"))); + +int __mingw_set_printf_count_output_fallback( int ); +int __mingw_set_printf_count_output_fallback( int mode ) +{ return 0; } + +#else +/* We want _get_output_format() to serve as our default compilation + * option, so deal with the "%n" availability controls, as provided by + * _get_printf_count_output() and _set_printf_count_output(), and also + * _set_output_format() compilation options first. + */ +#if defined __setnfmt__ +/* + * Here, we are wrapping the _set_printf_count_output() function... + */ +# define RTNTYPE int +# define ARGTYPE int +# define FUNCTION _set_printf_count_output +# define ARGLIST mode + +#define api_helper_result api_helper( mode ) +extern int __mingw_set_printf_count_output_fallback( int ); + +#elif defined __getnfmt__ +/* + * ...while here, it is _get_printf_count_output(). + */ +# define RTNTYPE int +# define ARGTYPE void +# define FUNCTION _get_printf_count_output +# define ARGLIST + +#define api_helper_result api_helper() +extern int __mingw_get_printf_count_output_fallback( void ); + +#elif defined __setofmt__ +/* + * This is our implementation for the _set_output_format() function, + * which will be called when there is no MSVCRT implementation. + */ +# define RTNTYPE unsigned int +# define FUNCTION _set_output_format +# define ARGTYPE unsigned int +# define ARGLIST style + +/* Our replacement function emulates the documented behaviour of + * its MSVCRT counterpart, assigning a new value for the recorded + * formatting style, then returning the previous setting. + */ +#define api_helper_result api_invoke( api_helper, style ) +static __inline__ __attribute__((__always_inline__)) unsigned int +api_invoke( unsigned int (*api_helper)(unsigned int), unsigned int style ) +{ + /* Note that we implement this, predominantly, inline within + * the wrapper code, using the fallback function pointer only + * to retrieve the previous value for return; thus it is able + * to use a handler in common with _get_output_format()... + */ + unsigned int retval = api_helper( style ); + __mingw_output_format_flag = style; + return retval; +} +/* ...while declaring its formal prototype as external. + */ +extern unsigned int __mingw_set_output_format_fallback( unsigned int ); + +#else /* __getofmt__, either explicitly defined, or by default */ +/* + * When not explicitly requested to implement _set_output_format(), + * assume __getofmt__ is implied, and implement _get_output_format(), + * for use when there is no MSVCRT implementation. + */ +# define RTNTYPE unsigned int +# define FUNCTION _get_output_format +# define ARGTYPE void +# define ARGLIST + +#define api_helper_result __mingw_output_format_flag = api_helper() +extern unsigned int __mingw_get_output_format_fallback( void ); +#endif + +/* Macros to facilitate a generic implementation for our replacement + * interface functions. + */ +#define __mingw_(FUNCTION) __construct_name__(__mingw,FUNCTION) +#define __fallback_(FUNCTION) __construct_name__(FUNCTION,_fallback) +#define __construct_name__(prefix,suffix) prefix##suffix + +/* Macros to map replacement function names to their equivalent + * symbol names, for lookup in the MSVCRT export table. + */ +#define __api_name__(FUNCTION) __stringify__(FUNCTION) +#define __stringify__(TEXT) #TEXT + +RTNTYPE __mingw_(FUNCTION)( ARGTYPE ARGLIST ) +{ + /* Our generic interface maps an indirect call to the API... + */ + static RTNTYPE (*api_helper)( ARGTYPE ARGLIST ) = NULL; + + /* ...such that it will prefer an MSVCRT implementation... + */ + if( (api_helper == NULL) + && ((api_helper = dlsym( RTLD_DEFAULT, __api_name__(FUNCTION) )) == NULL) ) + /* + * ...but substituting our replacement, when necessary... + */ + api_helper = __mingw_(__fallback_(FUNCTION)); + + /* ...and ultimately, invoking the selected API function. + */ + return api_helper_result; +} +#endif + +/* $RCSfile$: end of file */ diff --git a/mingwrt/mingwex/stdio/pformat.c b/mingwrt/mingwex/stdio/pformat.c index 5f8bbbb..c9dbf3e 100644 --- a/mingwrt/mingwex/stdio/pformat.c +++ b/mingwrt/mingwex/stdio/pformat.c @@ -149,38 +149,32 @@ * The following macro allows us to replicate this behaviour. */ # define PFORMAT_MINEXP __pformat_exponent_digits() - /* - * However, this feature is unsupported for versions of the - * MSVC runtime library prior to msvcr80.dll, and by default, - * MinGW uses an earlier version, (equivalent to msvcr60.dll), - * for which `_TWO_DIGIT_EXPONENT' will be undefined. - */ -# ifndef _TWO_DIGIT_EXPONENT - /* - * This hack works around the lack of the `_set_output_format()' - * feature, when supporting versions of the MSVC runtime library - * prior to msvcr80.dll; it simply enforces Microsoft's original - * convention, for all cases where the feature is unsupported. - */ -# define _get_output_format() 0 -# define _TWO_DIGIT_EXPONENT 1 -# endif /* - * Irrespective of the MSVCRT version supported, *we* will add - * an additional capability, through the following inline function, - * which will allow the user to choose his own preferred default - * for `PRINTF_EXPONENT_DIGITS', through the simple expedient - * of defining it as an environment variable. + * We note that Microsoft did not implement the _set_output_format() + * API capability until the release of MSVCR80.DLL, although they did + * subsequently retro-fit it to MSVCRT.DLL from Windows-Vista onward. + * To circumvent this API availability limitation, *we* offer two + * alternative options for controlling the facility: + * + * 1) We support the explicit assignment of the user's preference + * for `PRINTF_EXPONENT_DIGITS', through the simple expedient of + * defining it as an environment variable; this mechanism will + * take precedence over... + * + * 2) Emulation of _set_output_format(), through the use of inline + * functions defined in stdio.h, and supported regardless of the + * availability of the API within MSVCRT.DLL; this emulated API + * maintains state in the global `__mingw_output_format_flag' + * variable, (which users should consider to be private). */ +extern unsigned int __mingw_output_format_flag; static __inline__ __attribute__((__always_inline__)) int __pformat_exponent_digits( void ) { char *exponent_digits = getenv( "PRINTF_EXPONENT_DIGITS" ); return ((exponent_digits != NULL) && ((unsigned)(*exponent_digits - '0') < 3)) - || (_get_output_format() & _TWO_DIGIT_EXPONENT) - ? 2 - : 3 - ; + || (__mingw_output_format_flag & _TWO_DIGIT_EXPONENT) + ? 2 : 3 ; } #else /* diff --git a/mingwrt/ofmt_stub.s b/mingwrt/ofmt_stub.s deleted file mode 100644 index 81255b0..0000000 --- a/mingwrt/ofmt_stub.s +++ /dev/null @@ -1,40 +0,0 @@ -/* ofmt_stub.s - * - * $Id$ - * - * A trivial stub, to replace the _get_output_format() function. - * - * _pformat() requires this function, which is provided by MSVCRT runtimes - * from msvcr80.dll onwards; add this stub to the import libraries for earlier - * versions of MSVCRT, (those which do not already advertise availability of - * any exported _get_output_format() function); this will permit _pformat() - * to transparently interoperate with all supported versions of MSVCRT. - * (Likewise for CRTDLL). - * - * Written by Keith Marshall <kei...@us...> - * Contributed to the MinGW Project, and hereby assigned to the public domain. - * - * This is free software. It is provided AS IS, in the hope that it may be - * useful. There is NO WARRANTY OF ANY KIND, not even an implied warranty of - * merchantability, nor of fitness for any particular purpose. - * - */ - .text - .p2align 1,,4 - -.globl __get_output_format - .def __get_output_format; .scl 2; .type 32; .endef - -__get_output_format: -/* - * int _get_output_format( void ); - * - * Implementation is trivial: we immediately return zero, thus matching the - * default behaviour of Microsoft's own implementation, in the absence of any - * preceding call to _set_output_format(); (if we are using this stub, then - * that entire API is unsupported, so no such prior call is possible). - */ - xorl %eax, %eax - ret - -/* $RCSfile$$Revision$: end of file */ https://sf.net/p/mingw/mingw-org-wsl/ci/739d533acd49940efaf062a6ddbfb4a83526aed7/ commit 739d533acd49940efaf062a6ddbfb4a83526aed7 Author: Keith Marshall <kei...@us...> Date: Sun Nov 16 18:35:19 2014 +0000 Do some more essential build tree clean up. diff --git a/mingwrt/ChangeLog b/mingwrt/ChangeLog index b909330..03d165c 100644 --- a/mingwrt/ChangeLog +++ b/mingwrt/ChangeLog @@ -1,3 +1,10 @@ +2014-11-16 Keith Marshall <kei...@us...> + + Do some more essential build tree clean up. + + * Makefile.in (mostly-clean-local): Add *.libimpl; matching files get + in the way of a successful rebuild from clean. + 2014-11-11 Keith Marshall <kei...@us...> Implement wrappers for emulation of POSIX dlfcn API. diff --git a/mingwrt/Makefile.in b/mingwrt/Makefile.in index ed7c9f9..1e10ec5 100644 --- a/mingwrt/Makefile.in +++ b/mingwrt/Makefile.in @@ -818,7 +818,7 @@ clean-local: mostlyclean-local $(RM) msvcr*.def moldname*.def mingw*.def lib*.a *.dll mostlyclean-local: - $(RM) *.d *.$(OBJEXT) Makefile.stub + $(RM) *.d *.$(OBJEXT) Makefile.stub *.libimpl distclean-local: clean-local $(RM) config.log config.status libm_dummy.c https://sf.net/p/mingw/mingw-org-wsl/ci/e974c5e23a4213c6c9e22c6c812501cf35e30cd9/ commit e974c5e23a4213c6c9e22c6c812501cf35e30cd9 Author: Keith Marshall <kei...@us...> Date: Tue Nov 11 18:13:14 2014 +0000 Implement wrappers for emulation of POSIX dlfcn API. diff --git a/mingwrt/ChangeLog b/mingwrt/ChangeLog index 8406336..b909330 100644 --- a/mingwrt/ChangeLog +++ b/mingwrt/ChangeLog @@ -1,3 +1,25 @@ +2014-11-11 Keith Marshall <kei...@us...> + + Implement wrappers for emulation of POSIX dlfcn API. + + * include/dlfcn.h: New file; it declares the API. + * mingwex/dlfcn.c: New file; implement it, with access via... + (__mingw_dlfcn): ...this publicly addressable vector table. + + * Makefile.in (dlfcn.$OBJEXT): Add build requisite. + (dlopen, dlsym, dlclose, dlerror): Build call stubs, using... + (__LIBIMPL__): ...this automatic interface generator; implement it. + (libstub_refnames): New macro; it is adapted from and replaces... + (jmpstub_refs): ...this; when it is then invoked twice to build... + (Makefile.stub): ...this, it handles both JMPSTUB and LIBIMPL. + (jmpstub_awk_script): Adapt it to work with 'libstub_refnames'. + (libimpl_awk_script): New inline script; it handles automated builds + of LIBIMPL interfaces, as 'jmpstub_awk_script' does for JMPSTUB. + (libimpl_sed_script): New inline script; it prepares intermediate C + source code for LIBIMPL interfaces, as required to satisfy these... + (%.libimpl, %.libimpl.$OBJEXT): ...new implicit build objectives. + (LIBIMPL_CFLAGS): New macro; define it. + 2014-11-08 Keith Marshall <kei...@us...> Move libgen functions to __mingw_ pseudo-namespace. diff --git a/mingwrt/Makefile.in b/mingwrt/Makefile.in index 3cdb394..ed7c9f9 100644 --- a/mingwrt/Makefile.in +++ b/mingwrt/Makefile.in @@ -464,7 +464,7 @@ libmingwex.a: $(addsuffix .$(OBJEXT), _Exit atoll lltoa lltow \ ulltoa ulltow wtoll) libmingwex.a: gettimeofday.$(OBJEXT) -libmingwex.a: $(addsuffix .$(OBJEXT), dirent wdirent) +libmingwex.a: $(addsuffix .$(OBJEXT), dirent wdirent dlfcn) libmingwex.a: $(addsuffix .$(OBJEXT), fwide mbrtowc mbsinit wcrtomb wcstof \ wcstold wctob wmemchr wmemcmp wmemcpy wmemmove wmemset) @@ -490,21 +490,37 @@ $(addsuffix .$(OBJEXT), % %f %l): %_generic.c $(addsuffix .$(OBJEXT), llround llroundf llroundl): %.$(OBJEXT): lround_generic.c $(CC) -c -D FUNCTION=$* $(CPPFLAGS) $(ALL_CFLAGS) -o $@ $< +# Historically, MinGW.org's libm.a has been a dummy, delivering +# nothing of value; FIXME: IMO, this sucks; it should deliver the +# non-MSVCRT.DLL math functions, as noted above. +# +all-mingwrt-libs install-mingwrt-libs: libm.a +libm.a: libm_dummy.$(OBJEXT) +libm_dummy.c: Makefile + echo "static int __mingw_libm_dummy;" > $@ + # The mingwrt headers define a number of functions which are normally # expected to be compiled as inline code. Each such function must also # be provided with an externally visible entry point; we provide such # entry points as stubs in libmingwex.a, via the following rules: # sinclude Makefile.stub -jmpstub_refs = grep -lr '__JMPSTUB.*FUNCTION' -jmpstub_prerequisites := $(shell $(jmpstub_refs) ${mingwrt_srcdir}/include) -Makefile.stub: Makefile $(jmpstub_prerequisites) +vpath jmpstub.sx ${mingwrt_srcdir}/mingwex +libstub_refnames = grep -lr '__$1.*FUNCTION *=' ${mingwrt_srcdir} +jmpstub_prerequisites := $(shell $(call libstub_refnames,JMPSTUB)/include) +libimpl_prerequisites := $(shell $(call libstub_refnames,LIBIMPL)/include) +Makefile.stub: Makefile $(jmpstub_prerequisites) $(libimpl_prerequisites) echo "# $@: automatically generated file -- do not edit!" > $@ - $(jmpstub_awk_script) $(jmpstub_prerequisites) >> $@ + $(call jmpstub_awk_script,$(jmpstub_prerequisites)) >> $@ + $(call libimpl_awk_script,$(libimpl_prerequisites)) >> $@ echo "# $@: end of file" >> $@ -vpath jmpstub.sx ${mingwrt_srcdir}/mingwex -jmpstub_awk_script = test "$^" = Makefile || awk '\ +# Stubs are categorized into either of JMPSTUB or LIBIMPL classes; +# the rules for building the JMPSTUP inplementations are written to +# Makefile.stub, by processing each of their defining header files +# through the following awk script... +# +jmpstub_awk_script = test -z "$1" || awk '\ BEGIN { \ symbol = "([A-Z_a-z][A-Z_a-z0-9]*)"; \ fmt = "\nlib%s.a: %s\n%s: jmpstub.sx\n\t$$(COMPILE.sx) %s -o $$@ $$^\n"; \ @@ -512,22 +528,60 @@ jmpstub_awk_script = test "$^" = Makefile || awk '\ /__JMPSTUB(__)? *[(].*FUNCTION/ { \ LIB = "mingwex"; \ FUNCTION = gensub( ".*[ ,(:]FUNCTION *= *"symbol".*", "\\1", 1 ); \ - OBJNAME = gensub( "_*(.*)_*", "\\1", 1, FUNCTION )".stub.$$(OBJEXT)"; \ + OBJNAME = gensub( "_*(.*)_*", "\\1", 1, FUNCTION )".jmpstub.$$(OBJEXT)"; \ OBJNAME_CFLAGS = "-D FUNCTION="FUNCTION; \ printf fmt, LIB, OBJNAME, OBJNAME, OBJNAME_CFLAGS; \ } \ END { \ printf "\n"; \ - }' + }' $1 -all-mingwrt-libs install-mingwrt-libs: libm.a -# Historically, MinGW.org's libm.a has been a dummy, delivering -# nothing of value; FIXME: IMO, this sucks; it should deliver the -# non-MSVCRT.DLL math functions, as noted above. +# ...while this establishes the dependencies which apply for each +# of those in the LIBIMPL class. # -libm.a: libm_dummy.$(OBJEXT) -libm_dummy.c: Makefile - echo "static int __mingw_libm_dummy;" > $@ +libimpl_awk_script = test -z "$1" || awk '\ + BEGIN { \ + symbol = "([A-Z_a-z][A-Z_a-z0-9]*)"; \ + fmt = "\nlib%s.a: %s\n%s.libimpl: %s\n"; \ + } \ + /__LIBIMPL(__)? *[(].*FUNCTION/ { \ + LIB = "mingwex"; \ + FUNCTION = gensub( ".*[ ,(:]FUNCTION *= *"symbol".*", "\\1", 1 ); \ + OBJNAME = gensub( "_*(.*)_*", "\\1", 1, FUNCTION )".libimpl.$$(OBJEXT)"; \ + printf fmt, LIB, OBJNAME, FUNCTION, FILENAME; \ + } \ + END { \ + printf "\n"; \ + }' $1 + +# In contrast to JMPSTUB implementations, which are best handled +# by individual compilation rules in Makefile.stub, generic rules +# are sufficient for all LIBIMPL implementations; in each case, we +# filter the static attribute from each defined implementation, in +# its defining header file, to create an intermediate C source for +# each individual function; (note that we also remove any pragma +# which identifies the originating file as a system header). +# +libimpl_sed_script = sed \ + -e '/__CRT_ALIAS *__LIBIMPL.*FUNCTION *= *$1[ ,)].*)/d' \ + -e '/pragma .* system_header/d' + +LIBIMPL_CFLAGS = $(CFLAGS) $(INCLUDES) -fno-align-functions + +# LIBIMPL dependencies are tracked using zero sized name.libimpl +# files; the intermediate C files are generated as a side effect +# of resolving these dependencies... +# +%.libimpl: + $(call libimpl_sed_script,$*) $< > $@.c + > $@ + +# ...and ultimately discarded, after the requisite object file +# has been compiled. +# +%.libimpl.$(OBJEXT): %.libimpl + $(CC) -c $(CPPFLAGS) $(LIBIMPL_CFLAGS) -o $@ $<.c + $(RM) $<.c # Thread support libraries. # diff --git a/mingwrt/include/dlfcn.h b/mingwrt/include/dlfcn.h new file mode 100644 index 0000000..3653579 --- /dev/null +++ b/mingwrt/include/dlfcn.h @@ -0,0 +1,94 @@ +/* + * dlfcn.h + * + * Public interface declarations for (approximately) POSIX conforming + * dlopen(), dlsym(), dlclose(), and dlerror() API functions. + * + * $Id$ + * + * Written by Keith Marshall <kei...@us...> + * Copyright (C) 2014, MinGW.org Project + * + * + * 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, this permission notice, and the following + * disclaimer 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 OF OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ +#ifndef _DLFCN_H +#define _DLFCN_H 1 +#pragma GCC system_header + +#include <_mingw.h> + +/* POSIX requires the following four definitions. Our implementation + * (currently) does not support the behaviour associated with RTLD_LAZY; + * if specified, it is simply ignored; RTLD_NOW behaviour will prevail. + */ +#define RTLD_NOW 0 +#define RTLD_LAZY 1 +#define RTLD_GLOBAL 2 +#define RTLD_LOCAL 4 + +/* POSIX does not yet require the following pair, but reserves them for + * future capabilities; they will be used in contexts where a DLL module + * handle is expected, so we reserve values of suitable type, which are + * unlikely to ever occur as real module handles in practice. + */ +#define RTLD_DEFAULT (void *)(-1) +#define RTLD_NEXT (void *)(-3) + +/* The four dlfcn API functions, dlopen(), dlsym(), dlerror(), and dlclose(), + * are each defined privately, and made publicly accessible via corresponding + * function pointers, within the following publicly visible structure. + */ +_EXTERN_C struct __dlfcn__ +{ void *(*dlopen)( const char *, int ); + void *(*dlsym)( void *__restrict__, const char *__restrict__ ); + char *(*dlerror)( void ); + int (*dlclose)( void * ); +} __mingw_dlfcn; + +/* Declare the public API for each of these functions... + */ +_BEGIN_C_DECLS + +/* In any event, we always declare prototypes for all four functions. + */ +void * dlopen( const char *, int ); +void * dlsym( void *__restrict__, const char *__restrict__ ); +int dlclose( void * ); +char * dlerror( void ); + +__CRT_ALIAS __LIBIMPL__(( FUNCTION = dlopen )) +void *dlopen( const char *__name, int __mode ) +{ return __mingw_dlfcn.dlopen( __name, __mode ); } + +__CRT_ALIAS __LIBIMPL__(( FUNCTION = dlsym )) +void *dlsym( void *__restrict__ __module, const char *__restrict__ __name ) +{ return __mingw_dlfcn.dlsym( __module, __name ); } + +__CRT_ALIAS __LIBIMPL__(( FUNCTION = dlclose )) +int dlclose( void *__module ){ return __mingw_dlfcn.dlclose( __module ); } + +__CRT_ALIAS __LIBIMPL__(( FUNCTION = dlerror )) +char *dlerror( void ){ return __mingw_dlfcn.dlerror(); } + +_END_C_DECLS + +#endif /* _DLFCN_H: $RCSfile$: end of file */ diff --git a/mingwrt/mingwex/dlfcn.c b/mingwrt/mingwex/dlfcn.c new file mode 100644 index 0000000..59dbda2 --- /dev/null +++ b/mingwrt/mingwex/dlfcn.c @@ -0,0 +1,722 @@ +/* + * dlfcn.c + * + * Core implementation for (approximately) POSIX conforming dlopen(), + * dlsym(), dlclose(), and dlerror() API functions. + * + * $Id$ + * + * Written by Keith Marshall <kei...@us...> + * Copyright (C) 2014, MinGW.org Project + * + * + * 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, this permission notice, and the following + * disclaimer 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 OF OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * + * This source file implements the core functionality for each of the POSIX + * dynamic loader API functions, dlopen(), dlsym(), dlclose(), and dlerror(); + * each is implemented such that it is conveniently accessed via a macro, or + * an inline function representation, by way of a publicly visible table of + * entry point vectors (i.e. function pointers). + * + */ +#include <dlfcn.h> +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <stdint.h> +#include <limits.h> + +/* In addition to normal C runtime services, this implementation requires + * the use of the MS-Windows API; the light-weight subset will suffice... + */ +#define WIN32_LEAN_AND_MEAN +/* + * ...while accessing it throught its standard interface. + */ +#include <windows.h> + +/* In addition to the POSIX constants which are defined in dlfcn.h, + * we also define some private manifest constants, which POSIX does + * not specify, but which facilitate our implementation: + * + * RTLD_EXPLICIT, used to qualify RTLD_GLOBAL modules which have + * been explicitly loaded by dlopen(), so an RTLD_DEFAULT search + * need not consider them. + */ +# define RTLD_EXPLICIT 16 +/* + * RTLD_ALL_GLOBAL, returned by dlopen() when the module name is + * given as a NULL pointer; this is a fake module handle, similar + * to RTLD_DEFAULT and RTLD_NEXT, representing the corpus of all + * RTLD_GLOBAL modules, both implicitly and explicitly loaded. + */ +# define RTLD_ALL_GLOBAL (void *)(-5) + +/* Before anything else, ensure that the dlerror() implementation + * is in place, so that other components may access it freely. + * + * We provide TWO reference pointers for error message buffering, so + * that we may continue to hold a reference to allocated memory, even + * after dlerror() has discarded the pointer to a pending message; + * initially, we mark both as unassigned. + */ +static char *dlfcn_error_pending = NULL; +static char *dlfcn_error_message = NULL; + +static void dlfcn_store_error_message( const char *fmt, ... ) +{ + /* This private function provides printf() style formatting for + * dlfcn error messages, storing them into dynamically allocated + * memory, and updating both reference pointers for subsequent + * retrieval by the dlerror() accessor function. + */ + int msglen; + va_list argv; + va_start( argv, fmt ); + msglen = 1 + vsnprintf( NULL, 0, fmt, argv ); + if( (dlfcn_error_pending = realloc( dlfcn_error_message, msglen )) != NULL ) + /* + * Store message, only if a buffer was successfully allocated. + */ + vsnprintf( dlfcn_error_pending, msglen, fmt, argv ); + dlfcn_error_message = dlfcn_error_pending; + va_end( argv ); +} + +static char *dlfcn_strerror( int errcode ) +{ + /* This private function emulates strerror(), but substitutes an + * errcode obtained from GetLastError() for errno. + */ + char *text; + uint32_t description = FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER + | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, + errcode, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), (char *)(&text), + 0, NULL + ); + if( description ) + { + /* We successfully obtained an error description; POSIX insists + * that it must not be encoded with any terminating newline. + */ + char *tmp = text + strlen( text ); + while( (*--tmp == '\n') || (*tmp == '\r') ) *tmp = '\0'; + + /* Ensure that we consistently return the error description in + * a buffer which may be released by the free() function, (since + * we cannot be sure that LocalFree() is compatible). + */ + text = strdup( tmp = text ); + LocalFree( tmp ); + } + else + { /* We were unable to find an error description; substitute a + * formatted reference to the unknown error code. + */ + char *fmt = "Unknown error %d"; + char tmp[1 + snprintf( NULL, 0, fmt, errcode )]; + snprintf( tmp, sizeof( tmp ), fmt, errcode ); + text = strdup( tmp ); + } + /* However we derived it, the error description is now available + * in a dynamically allocated text buffer which may be released + * by calling free(). + */ + return text; +} + +static char *dlerror_internal( void ) +{ + /* This is the internal implementation of the public dlerror() API. + * POSIX does not require this to be thread safe, so we take no care + * to make it such. + */ + if( (dlfcn_error_pending == NULL) && (dlfcn_error_message != NULL) ) + { + /* There is no pending message, but a buffer remains allocated + * to one which has already been retrieved; we may release it. + */ + free( dlfcn_error_message ); + dlfcn_error_message = NULL; + } + /* Mark any pending error message as "retrieved"... + */ + dlfcn_error_pending = NULL; + /* + * ...and return it. + */ + return dlfcn_error_message; +} + +typedef +/* A pointer type, representing a reference to a function for + * retrieving exported symbol references from any loaded module. + */ +int WINAPI (*lookup_fn)( HANDLE, HMODULE *, uint32_t, uint32_t * ); + +/* POSIX requires that dlsym() be able to look up symbol references + * for any dynamically loaded module within the address space of the + * calling process, including those which have been implicitly loaded + * at process start-up. To facilitate this, we will use Microsoft's + * PSAPI.DLL services, to enumerate such implicitly loaded modules, + * before we explicitly load any using dlopen(), or we perform any + * look-up using dlsym() itself. + */ +static struct +{ /* Structure providing a convenient encapsulation of the PSAPI.DLL + * interface, which may itself need to be explicitly loaded. + */ + HMODULE dll; + const char *dll_name; + const char *lookup_fn_name; + lookup_fn lookup; +} +psapi = { NULL, "psapi.dll", "EnumProcessModules", NULL }; + +/* The two NULL fields, within the preceding structure, must be + * initialized before use; the following pair of inline functions + * facilitate this, with the first looking up the API entry... + */ +static __inline__ lookup_fn psapi_lookup_fn( HMODULE provider ) +{ return (lookup_fn)(GetProcAddress( provider, psapi.lookup_fn_name )); } + +/* ...when called from within the second, which both ensures that + * PSAPI.DLL is loaded, and assigns the requisite field values. + */ +static __inline__ lookup_fn psapi_lookup_fn_init( void ) +{ return psapi.lookup = ((psapi.dll = LoadLibrary( psapi.dll_name )) != NULL) + ? psapi_lookup_fn( psapi.dll ) : NULL; +} + +/* After the PSAPI.DLL interface has been initialized, by calling + * the preceding function, we may call the following inline helper + * to retrieve a list of module handles... + */ +static __inline__ unsigned int psapi_enum_modules( HMODULE **modules ) +{ + /* ...focussing on modules which are currently loaded within + * the address space of the current process. + */ + HANDLE me = GetCurrentProcess(); + + /* We will dynamically allocate memory to store the handles; + * initially we request zero bytes of storage to receive them, + * then we use a while loop, which should execute exactly twice, + * to retrieve them; on the first pass, we determine the actual + * memory requirement and allocate it; on the second, we store + * the list of module handles into it... + */ + uint32_t wanted, request = 0; + while( psapi.lookup( me, *modules, request, &wanted ) && (wanted > request) ) + if( (*modules = realloc( *modules, request = wanted )) == NULL ) + { + /* ...trying to record an appropriate diagnostic message on + * failure, (but noting that failure is likely to result from + * insufficient memory, so there may not be enough to record + * the message either)... + */ + char *reason = dlfcn_strerror( ERROR_OUTOFMEMORY ); + dlfcn_store_error_message( "dlfcn_init:enum_modules: %s", reason ); + free( reason ); + } + /* ...before ultimately returning the number retrieved. + */ + return wanted / sizeof( HMODULE ); +} + +typedef struct dltab +{ + /* Structure used to map module handle references, and associated + * status, into the global modules list. + */ + unsigned int slots; + unsigned char *flags; + HMODULE *modules; +} dltab; + +/* The global modules list itself, initially empty, but suitable + * for reallocation on the heap. + */ +static dltab rtld = { 0, NULL, NULL }; + +/* Microsoft's LoadLibrary() API is explicitly documented as being + * unable to handle regular slashes as directory separators in module + * path names, (in spite of their validity elsewhere), thus... + */ +static __inline__ wchar_t *normalized_form( wchar_t *pathname ) +{ + /* ...we provide this helper, to replace them with backslashes. + */ + wchar_t *scan = pathname; + do { if( *scan == L'/' ) *scan = L'\\'; } while( *scan++ != L'\0' ); + return pathname; +} + +static void dlopen_store_error_message( const char *name, unsigned status ) +{ + /* A convenience helper, to record diagnostic messages to explain + * causes of failure encountered when calling dlopen(). + */ + char *reason = dlfcn_strerror( status ); + dlfcn_store_error_message( "dlopen:'%s': %s", name, reason ); + free( reason ); +} + +static void *dlopen_internal( const char *name, int mode ) +{ + /* This is the formal implementation of the public dlopen() function; + * note that this (currently) ignores the RTLD_LAZY loading option, and + * processes each request as if specified as RTLD_NOW. + */ + if( name == NULL ) + /* POSIX specifies this as a special case, requiring us to return + * a handle, through which all symbols exported by all modules which + * are currently loaded with the RTLD_GLOBAL attribute, (explicitly + * or implicitly), would be accessible. + */ + return RTLD_ALL_GLOBAL; + + /* We will use LoadLibrary() to obtain a handle for the dlopen()ed + * module; Microsoft advise us that we should ensure that we always + * use backslashes, and to avoid use of POSIX compatible slashes, in + * any path name passed to this function. To ensure this, the name + * argument should be normalized, (working in the UTF-16LE domain, + * to avoid inadvertent transformation of trail bytes within MBC + * sequences), to transform slashes accordingly. + */ + size_t buflen; wchar_t internal_name[buflen = 1 + strlen( name )]; + MultiByteToWideChar( CP_ACP, 0, name, -1, internal_name, buflen ); + + /* Now, we may safely call LoadLibrary(), to obtain a module handle. + */ + void *module = LoadLibraryW( normalized_form( internal_name ) ); + if( module == NULL ) + { + /* The named module could not be opened; record an appropriate + * error message for retrieval by dlerror(). + */ + dlopen_store_error_message( name, GetLastError() ); + } + else + { /* We got a handle for the requested module; we need to ensure + * that it is allocated a slot in our global symbol table, but we + * must first check that it isn't already present. + */ + int index, insertion_point = rtld.slots; + for( index = 0; index < rtld.slots; index++ ) + { + /* As we scan the list of already loaded modules, check for any + * existing slot which may be vacant... + */ + if( rtld.flags[index] == 0 ) + { + /* ...marking the first available, if any, as a candidate for + * possible insertion of a new entry. + */ + if( index < insertion_point ) + insertion_point = index; + } + else if( module == rtld.modules[index] ) + { + /* The requested module appears to be loaded already; calling + * LoadLibrary() will have increased its reference count, but + * our management strategy doesn't require this; reduce it... + */ + FreeLibrary( module ); + /* + * ...but promote its existing status to RTLD_GLOBAL, if that + * is indicated as required by the requested mode... + */ + rtld.flags[index] |= mode & RTLD_GLOBAL; + /* + * ...and immediately return the module handle. + */ + return module; + } + } + /* If we get to here, there is no entry for the requested module, + * within the global modules list; we must add it now, either using + * an existing vacant slot, if there is one... + */ + if( insertion_point < rtld.slots ) + { + /* ...but noting that we cannot simply insert a new reference + * within it, since that would disrupt the order in which modules + * are subsequently searched, and POSIX requires that this is to + * preserve loading order, (strictly, symbol resolution order), + * when searching RTLD_GLOBAL modules, (and since any module may + * be promoted to RTLD_GLOBAL status, even after it was originally + * loaded as RTLD_LOCAL, this means that we must preserve loading + * order for ALL active modules). Thus, we must pack the list of + * active modules after the nominated insertion point... + */ + index = insertion_point; + + /* ...after which, the first vacant slot will have been relocated + * to follow all active slots, and we may adjust the nominated + * insertion point accordingly. + */ + do { /* First, we identify the first slot following the nominated + * insertion point, which is NOT vacant. + */ + while( (index < rtld.slots) && (rtld.flags[index] == 0) ) + ++index; + + /* Now, we move that non-vacant slot, and any which follow + * it, upwards in the list, to fill vacant slots... + */ + while( (index < rtld.slots) && (rtld.flags[index] != 0) ) + { + /* ...by simply copying content from the non-vacant slots + * to overwrite content in the preceding slots... + */ + rtld.modules[insertion_point] = rtld.modules[index]; + rtld.flags[insertion_point++] = rtld.flags[index++]; + } + /* ...repeating the entire procedure, until all vacant slots + * have been filled, and the nominated insertion point has + * been moved to follow the last relocated non-vacant entry. + */ + } while( index < rtld.slots ); + + /* After packing, any already allocated slots after and including + * the relocated insertion point MUST be vacant; ensure that they + * are marked accordingly. + */ + for( index = insertion_point; index < rtld.slots; index++ ) + rtld.flags[index] = 0; + } + else + { /* There is no vacant slot: we must expand the allocated memory + * pool to create one; first increment the modules list size... + */ + size_t slots = 1 + rtld.slots; + HMODULE *modules = rtld.modules; + if( (modules = realloc( modules, sizeof( HMODULE ) * slots )) != NULL ) + { + /* ...and, having sucessfully increased the modules list memory + * allocation, do likewise for the associated flags... + */ + unsigned char *flags = rtld.flags; + if( (flags = realloc( flags, slots )) != NULL ) + /* + * ...initializing the new flags register, and incrementing + * the slots count, when that is also successful. + */ + (rtld.flags = flags)[rtld.slots++] = 0; + + /* Regardless of the success, or otherwise, of the flags memory + * adjustment, the modules list was reallocated, so we need to + * adjust its reference pointer accordingly. + */ + rtld.modules = modules; + } + /* Before proceeding further, verify that the new slot has been + * fully created, and is ready to store the module data... + */ + if( insertion_point == rtld.slots ) + { + /* ...but if allocation failed, the nominated insertion point + * will lie beyond the available space, so we MUST fail, after + * discarding the now unreferenced module handle, and trying to + * record an appropriate diagnostic message, (but we note that + * this may also fail, due to insufficient memory). + */ + FreeLibrary( module ); + dlopen_store_error_message( name, ERROR_OUTOFMEMORY ); + return NULL; + } + } + /* When we get to here, we now have a suitable slot in which to add + * the reference data for the newly dlopen()ed module, (either by + * allocation of a new slot, or by relocation of an existing vacant + * slot); we may safely store the appropriate reference data. + */ + rtld.flags[insertion_point] = RTLD_EXPLICIT | mode; + rtld.modules[insertion_point] = module; + } + /* Finally, we return whatever module handle we got from LoadLibrary(), + * (which may be NULL, if this failed). + */ + return module; +} + +static void *dlsym_internal( void *module, const char *name ) +{ + /* This is the formal implementation of the public dlsym() function. + */ + static unsigned int index = 0; + static const char *last_named_symbol = NULL; + static unsigned char rtld_exclude = 0; + void *rtn; + + if( module == RTLD_NEXT ) + { + /* NOTE: We MUST identify this special case BEFORE any other! + * + * POSIX doesn't require this to be supported yet, but reserves it + * for future use; it should cause dlsym() to repeat its search for + * the named symbol, (provided it remains the same as last named in + * a global search), continuing from the next module in the current + * search order, following that in which the symbol was previously + * found; (this permits us to locate symbols which may defined in + * more than one loaded module). + */ + index = (name == last_named_symbol) ? index + 1 : 0; + module = RTLD_ALL_GLOBAL; + } + else if( module == RTLD_DEFAULT ) + { + /* NOTE: We MUST keep this AFTER the check for RTLD_NEXT! + * + * Once again, POSIX doesn't require this to be supported yet, but + * reserves it for future use to search for symbols which could have + * been found within the process address space prior to any explicit + * dlopen() call; this capability may be supported by searching all + * modules in the address space, (i.e. equivalent to a search with + * module == RTLD_ALL_GLOBAL), excluding those which have been + * explicitly loaded since process start-up. + */ + module = RTLD_ALL_GLOBAL; + rtld_exclude = RTLD_EXPLICIT; + index = 0; + } + else + /* Neither RTLD_DEFAULT, nor RTLD_NEXT was specified; we must reset + * the RTLD_GLOBAL search index, and cancel all search exclusions. + */ + index = rtld_exclude = 0; + + if( module == RTLD_ALL_GLOBAL ) + { + /* The RTLD_ALL_GLOBAL module reference represents a request to + * perform an in-order traversal of all modules within the process + * address space, either implicitly loaded, or explicitly loaded + * with the RTLD_GLOBAL mode attribute, either until the named + * symbol is found, or all such modules have been searched + * without finding it. + */ + for( rtn = NULL; (rtn == NULL) && (index < rtld.slots); index++ ) + if( ((rtld_exclude & rtld.flags[index]) == 0) + && ((rtld.flags[index] & RTLD_GLOBAL) == RTLD_GLOBAL) ) + rtn = GetProcAddress( rtld.modules[index], name ); + + /* Note the symbol named in the current search, so that we may + * check for consistency in the event that the next search is + * invoked as an RTLD_NEXT request. + */ + last_named_symbol = name; + } + else + { /* When a specific module reference is specified, confine the + * search to the specified module alone... + */ + rtn = GetProcAddress( (HMODULE)(module), name ); + + /* ...and inhibit any attempt to follow this search with one + * specifying RTLD_NEXT; (this would not be valid, since there + * is no concept of a "next" module to be searched, when not + * searching through an ordered list of modules). + */ + last_named_symbol = NULL; + } + + if( rtn == NULL ) + { + /* The named symbol was not found in any module which was searched; + * record the appropriate error message for retrieval by dlerror(). + */ + char *reason = dlfcn_strerror( GetLastError() ); + dlfcn_store_error_message( "dlsym:'%s': %s", name, reason ); + free( reason ); + } + + /* Return the symbol address, as assigned to the return value; + * (this will be NULL, if the named symbol was not found). + */ + return rtn; +} + +static int dlclose_store_error_message( int status ) +{ + /* A private helper function to record an appropriate dlerror() + * message, on failure of dlclose(). + */ + char *reason = dlfcn_strerror( status = GetLastError() ); + dlfcn_store_error_message( "dlclose: %s", reason ); + free( reason ); + return status; +} + +static __inline__ int dlclose_internal_check_for_error( int status ) +{ + /* A private helper function to set the return status for dlclose(), + * while also recording a dlerror() message, when status is "failed". + */ + return (status == 0) ? dlclose_store_error_message( status ) : 0; +} + +static int dlclose_internal( void *module ) +{ + /* This is the formal implementation of the public dlclose() function; + * it will call Microsoft's FreeLibrary() function passing the specified + * module handle, provided this is listed in the global module table as + * having been explicitly opened by our dlopen() function. + */ + int index; + for( index = 0; index < rtld.slots; index++ ) + if( module == rtld.modules[index] ) + { + /* The specified module handle is present in the global modules list; + * while we could simply call FreeLibrary() immediately, it may not be + * prudent to do so in respect of implicitly loaded modules, but for + * those which we have explicitly loaded... + */ + if( ((rtld.flags[index] & RTLD_EXPLICIT) == RTLD_EXPLICIT) + /* + * ...and which can be successfully released by FreeLibrary()... + */ + && (dlclose_internal_check_for_error( FreeLibrary( module )) == 0) ) + /* + * ...we mark them as no longer available for dlsym() processing, + * and return immediately, indicating success... + */ + return rtld.flags[index] = 0; + + /* ...but when we didn't successfully release the module, we have + * no need to continue the search for its handle in the global list + * of modules, (because we've already found it); we may immediately + * abandon the search. + */ + break; + } + + /* If we get to here, we either didn't find the specified module handle in + * the global list of modules, or we found it but were unable to release + * it; in either case, we force a module error condition. + */ + return dlclose_store_error_message( FreeLibrary( NULL ) ); +} + +static void dlfcn_init( void ) +{ + /* This private initialization function must be called, as a + * prerequisite to the first use of either dlopen() or dlsym() + * in any process; it uses Microsoft's PSAPI.DLL interface to + * enumerate the implicitly loaded process modules, so that + * they may be searched implicitly... [truncated message content] |