OSDN Git Service

Implement a generic legacy platform support infrastructure.
authorKeith Marshall <keith@users.osdn.me>
Sun, 23 May 2021 19:54:29 +0000 (20:54 +0100)
committerKeith Marshall <keith@users.osdn.me>
Sun, 23 May 2021 19:54:29 +0000 (20:54 +0100)
mingwrt/ChangeLog
mingwrt/Makefile.in
w32api/ChangeLog
w32api/Makefile.in
w32api/include/legacy.h [new file with mode: 0644]
w32api/lib/availapi.c [new file with mode: 0644]

index 6d41e13..345bd47 100644 (file)
@@ -1,3 +1,10 @@
+2021-05-23  Keith Marshall  <keith@users.osdn.me>
+
+       Add a makefile clarification comment.
+
+       * Makefile.in (msvcrt_repl_funcs): Explain inclusion...
+       (free, realloc): ...these.
+
 2021-04-11  Keith Marshall  <keith@users.osdn.me>
 
        Prepare and publish MinGW.org WSL-5.4.2 release.
index 6c562e8..dc0c8c7 100644 (file)
@@ -270,6 +270,12 @@ $(addsuffix .def,$(all_msvcrt)): %.def: msvcrt.def.in
 #
 msvcrt_repl_prefix := __msvcrt
 msvcrt_repl_funcs := printf fprintf sprintf vprintf vfprintf vsprintf
+
+# Likewise, for the MinGW wrappers which allow free() and realloc()
+# to operate on both regularly-aligned, and on over-aligned, blocks
+# of heap memory; (Microsoft require distinct functions for their
+# implementations of regularly-aligned and over-aligned allocation).
+#
 msvcrt_repl_funcs += free realloc
 
 # This is kludgey, but dlltool lacks the selectivity to do the job
index 5a50095..ec156e7 100644 (file)
@@ -1,3 +1,24 @@
+2021-05-23  Keith Marshall  <keith@users.osdn.me>
+
+       Implement a generic 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.
+
 2021-05-09  Keith Marshall  <keith@users.osdn.me>
 
        Correct TreeView_GetItemRect() syntax; cf. MinGW-Issue #41041.
index ba39f7b..9f33f32 100644 (file)
@@ -7,7 +7,7 @@ PACKAGE_TARNAME := @PACKAGE_TARNAME@
 PACKAGE_VERSION := @PACKAGE_VERSION@
 
 # Written by Keith Marshall <keithmarshall@users.sourceforge.net>
-# 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
@@ -125,6 +125,16 @@ lib%.a: %.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)
diff --git a/w32api/include/legacy.h b/w32api/include/legacy.h
new file mode 100644 (file)
index 0000000..f0ec3e9
--- /dev/null
@@ -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 <keith@users.osdn.me>
+ * 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 <winbase.h>, 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 <winbase.h>
+
+_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 <winerror.h>; DO NOT define it conditionally, since
+ * that would deny the compiler an opportunity to verify that it
+ * is a faithful duplicate, if <winerror.h> 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 (file)
index 0000000..0c899ac
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * 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 <keith@users.osdn.me>
+ * 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;
+}
+#else
+/* None of the mandatory -D_spec arguments have been specified; we need
+ * at least one of...
+ */
+# error "A -D_lib=DLLNAME, -D_bound, or -D_unbound argument is required."
+#endif
+
+/* $RCSfile$: end of file */