# HG changeset patch # Date 1621722798 -3600 # Parent 381c6e5f1725d429b9a5481252be4d56a4860853 Implement generalized legacy platform support infrastructure. * lib/availapi.c: New file; it implements... (__kernel32_entry_point, __bound_dll_entry_point) (__unbound_dll_entry_point): ...these run-time link helper functions. * include/legacy.h: New file. (__kernel32_entry_point, __bound_dll_entry_point) (__unbound_dll_entry_point): Declare them. (__legacy_support): New inline error notifier; implement it. (API_UNCHECKED, API_UNSUPPORTED): Define them. (ERROR_OLD_WIN_VERSION): Duplicate. * Makefile.in (libkernel32.a): Integrate... (k32entry.$OBJEXT, bound.$OBJECT): ...these; they provide... (__kernel32_entry_point, __bound_dll_entry_point): ...these; add build rules, as appropriate, incorporating... (NO_ALIGN_FLAGS): ...this new macro. diff --git a/w32api/Makefile.in b/w32api/Makefile.in --- a/w32api/Makefile.in +++ b/w32api/Makefile.in @@ -5,11 +5,11 @@ PACKAGE_TARNAME := @PACKAGE_TARNAME@ PACKAGE_VERSION := @PACKAGE_VERSION@ # Written by Keith Marshall -# Copyright (C) 2014-2017, MinGW.org Project +# Copyright (C) 2014-2017, 2021, 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 @@ -123,10 +123,20 @@ all-w32api-libs install-w32api-libs: lib lib%.a: %.def $(DLLTOOL) --as=$(AS) -k --output-lib $@ --def $< $(if $(filter-out $<,$^),$(AR) $(ARFLAGS) $@ $(filter-out $<,$^)) vpath %.c ${srcdir}/lib +libkernel32.a: k32entry.$(OBJEXT) bound.$(OBJEXT) + +NO_ALIGN_FLAGS := -fno-align-jumps -fno-align-functions +bound.$(OBJEXT) unbound.$(OBJEXT): %.$(OBJEXT): availapi.c + $(CC) -c $(ALL_CFLAGS) $(NO_ALIGN_FLAGS) -D_$* $< -o $@ + +bound_dll_api_list := k32entry +$(addsuffix .$(OBJEXT),$(bound_dll_api_list)): %.$(OBJEXT): availapi.c + $(CC) -c $(ALL_CFLAGS) $(NO_ALIGN_FLAGS) -D_bound -D_lib=$* $< -o $@ + libuuid.a: ativscp-uuid.$(OBJEXT) cguid-uuid.$(OBJEXT) libuuid.a: comcat-uuid.$(OBJEXT) devguid.$(OBJEXT) docobj-uuid.$(OBJEXT) libuuid.a: exdisp-uuid.$(OBJEXT) extras-uuid.$(OBJEXT) hlguids-uuid.$(OBJEXT) libuuid.a: hlink-uuid.$(OBJEXT) mlang-uuid.$(OBJEXT) mshtml-uuid.$(OBJEXT) libuuid.a: msxml-uuid.$(OBJEXT) oaidl-uuid.$(OBJEXT) objidl-uuid.$(OBJEXT) diff --git a/w32api/include/legacy.h b/w32api/include/legacy.h new file mode 100644 --- /dev/null +++ b/w32api/include/legacy.h @@ -0,0 +1,98 @@ +/* + * legacy.h + * + * Run-time binding helper routines, to facilitate access to APIs which + * may not be universally supported, while allowing for graceful fall-back + * action, when running on legacy Windows versions. + * + * $Id$ + * + * Written by Keith Marshall + * Copyright (C) 2021, 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 and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ +#ifndef _LEGACY_H +#define _LEGACY_H + +/* Dynamic legacy support is dependent of standard Windows-API + * features, which are declared in , and implemented + * in kernel32.dll; the supplementary API helper functions, which + * are declared herein, are all implemented as extensions within + * libkernel32.a, whence they will be statically linked. + */ +#include + +_BEGIN_C_DECLS + +/* Manifest constants to represent the resolution state of any + * DLL entry-point; this is assumed to be recorded in a static + * "void *" pointer, specific to each entry-point, initialized + * to "API_UNCHECKED", and passed to the resolver, whence the + * return value, (which may be either an actual entry-point + * function pointer, or "API_UNSUPPORTED"), should be assigned + * in place of the initial "API_UNCHECKED" value. + */ +#define API_UNCHECKED (void *)(-1) +#define API_UNSUPPORTED (void *)(0) + +/* The following is a duplicate of the error code, as nominally + * defined in ; DO NOT define it conditionally, since + * that would deny the compiler an opportunity to verify that it + * is a faithful duplicate, if is included first. + */ +#define ERROR_OLD_WIN_VERSION 1150L + +/* DLL-specific entry-point resolvers; declare as "pure", to + * avoid GCC's penchant for burdening the code with unnecessary + * register saves, and restores of memory, at point of call. + */ +extern __attribute__((pure)) +void *__kernel32_entry_point (void *, const char *); + +/* Entry-point resolvers for named DLLs, (explicitly bound at + * link-time, or dynamically loaded, respectively); declared as + * "pure" for same reason as above. + */ +extern __attribute__((pure)) +void *__bound_dll_entry_point (void *, const char *, const char *); + +/* Whereas the preceding resolver assumes that the DLL named by + * its first "const char *" argument has been explicitly bound at + * link-time, (and will return "API_UNSUPPORTED" if it has not), + * the following will load the DLL if necessary, (but it will NOT + * increment the reference count, if the DLL is already mapped + * into the process address space). + */ +extern __attribute__((pure)) +void *__unbound_dll_entry_point (void *, const char *, const char *); + +/* The following helper function, which is ALWAYS expanded in-line, + * provides a convenient mechanism for returning an error status code, + * while also setting said code as Windows last error. + */ +__CRT_ALIAS int __legacy_support( int status ) +{ SetLastError( status ); return status; } + +_END_C_DECLS + +#endif /* !_LEGACY_H: $RCSfile$: end of file */ diff --git a/w32api/lib/availapi.c b/w32api/lib/availapi.c new file mode 100644 --- /dev/null +++ b/w32api/lib/availapi.c @@ -0,0 +1,163 @@ +/* + * availapi.c + * + * Provides generic DLL entry-point lookup helper functions, to facilitate + * run-time linking of API functions which may not be supported in legacy + * versions of Windows. + * + * $Id$ + * + * Written by Keith Marshall + * Copyright (C) 2021, 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 and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * + * Compile this module multiple times, once for each entry-point resolver + * which is required; the specifics of the resolver are determined thus: + * + * $ gcc -c -D_bound availapi.c -o bound.o + * + * will create a generic resolver, named __bound_dll_entry_point(), which + * will resolve entry-points ONLY within DLLs which have been explicitly + * loaded beforehand. Conversely: + * + * $ gcc -c -D_unbound availapi.c -o unbound.o + * + * will create a generic resolver, named __unbound_dll_entry_point(); this + * will attempt to load a named DLL, if it is not already mapped, before it + * attempts to resolve a named entry-point within it. Finally: + * + * $ gcc -c -D_lib=DLLNAME [-D_bound] availapi.c -o dllentry.o + * + * will create a resolver specific to the DLL specified by DLLNAME, (the + * file-name only part of the DLL name, WITHOUT either the ".dll" suffix, + * or any preceding directory path qualification; this resolver will be + * named __DLLNAME_entry_point(). If the "-D_lib=DLLNAME" specification + * is accompanied by the optional "-D_bound" flag, this resolver will be + * implemented as a thin wrapper around __bound_dll_entry_point(); OTOH, + * if the "-D_bound" flag is not specified, it will be implemented as a + * thin wrapper around __unbound_dll_entry_point(). + * + */ +#include "legacy.h" + +#if defined _lib +/* The entry-point resolver is to be associated with a specifically + * named DLL; define a set of mappings between preferred object file + * names (aliases), and their associated DLL names; (note that this + * facility is primarily provided to accommodate makefile mapping of + * resolver object file names to DLL names; the DLLNAME reference, + * within the command line "-D_lib=DLLNAME" specification, is given + * as the alias, but within the resolver FUNCTION name, it is ALWAYS + * set to match the "DLL Name" entry from the following table): + * + * Alias DLL Name + * -------- -------- */ +# define k32entry kernel32 + +/* Provide a set of macros, to derive the entry-point resolver name, + * and its associated DLL name, from the command line assignment for + * the object file's base name: + */ +# define _dll(_lib) _as_string(_lib) ".dll" +# define _entry(_lib) __##_lib##_entry_point +# define _entry_point(_lib) _entry(_lib) +# define _as_string(_name) #_name + +/* Implement the appropriately named entry-point resolver function... + */ +void *_entry_point(_lib) (void *hook, const char *procname) +# if defined _bound +{ /* ...in terms of the appropiate resolver for a DLL which is + * expected to have been implicitly loaded, (i.e. explicitly + * bound to the executable, at link-time)... + */ + return __bound_dll_entry_point( hook, _dll(_lib), procname ); +} +# else +{ /* ...or otherwise, for a DLL which MAY need to be explicitly + * loaded, on demand. + */ + return __unbound_dll_entry_point( hook, _dll(_lib), procname ); +} +# endif +#elif defined _bound +/* This entry-point resolver is to be generic, w.r.t. the DLL name + * with which it will be associated, but will require that the named + * DLL has been explicitly bound to the application, at link-time. + */ +void *__bound_dll_entry_point +( void *hook, const char *dllname, const char *procname ) +{ + /* If the passed entry-point hook has already been assigned, then + * there is nothing more to do, other than to return it... + */ + if( hook == API_UNCHECKED ) + { /* ...otherwise, we perform a DLL entry-point lookup, considering + * only DLLs which are already mapped into the address space of the + * calling process, and subsequently updating the hook to represent + * the entry-point, or mark it as permanently unsupported. + */ + HMODULE dll = GetModuleHandleA( dllname ); + hook = (dll == NULL) ? GetProcAddress( dll, procname ) : API_UNSUPPORTED; + } + /* In any case, we return the (possibly updated) hook, which should + * then be recorded by the caller. + */ + return hook; +} +#elif defined _unbound +/* This entry-point resolver performs a similar function to that above, + * except that it will attempt to explicitly load any named DLL which is + * not already mapped into the address space of the calling process. + */ +void *__unbound_dll_entry_point +( void *hook, const char *dllname, const char *procname ) +{ + /* If the passed entry-point hook has already been assigned, then + * there is nothing more to do, other than to return it... + */ + if( hook == API_UNCHECKED ) + { /* ...otherwise, we perform a DLL entry-point lookup, loading + * the named DLL, if it has not yet been mapped into the address + * space of the calling process... + */ + HMODULE dll = GetModuleHandleA( dllname ); + if( (dll == NULL) && ((dll = LoadLibraryA( dllname )) == NULL) ) + /* + * ...marking the hook as permanently unsupported, in the + * event of failure to map the DLL... + */ + return hook = API_UNSUPPORTED; + + /* ...otherwise, updating it to reflect the lookup result. + */ + hook = GetProcAddress( dll, procname ); + } + /* In any case, we return the (possibly updated) hook, which should + * then be recorded by the caller. + */ + return hook; +} +#endif + +/* $RCSfile$: end of file */