]> granicus.if.org Git - vim/commitdiff
patch 8.2.4317: MS-Windows: Vim exits when Python 3 initialisation fails v8.2.4317
authorBram Moolenaar <Bram@vim.org>
Mon, 7 Feb 2022 13:54:01 +0000 (13:54 +0000)
committerBram Moolenaar <Bram@vim.org>
Mon, 7 Feb 2022 13:54:01 +0000 (13:54 +0000)
Problem:    MS-Windows: Vim exits when Python 3 initialisation fails.
Solution:   Hook into the exit() function to recover from the failure.
            (Ken Takata, closes #9710)

src/errors.h
src/if_python3.c
src/os_win32.c
src/proto/os_win32.pro
src/version.c

index 80a9a5aaed2857121a80ed4ba64a895280e54aae..53bd0cd26cdce1be5576409709f2dc43e9fc8332 100644 (file)
@@ -3224,3 +3224,7 @@ EXTERN char e_autoload_import_cannot_use_absolute_or_relative_path[]
 EXTERN char e_cannot_use_partial_here[]
        INIT(= N_("E1265: Cannot use a partial here"));
 #endif
+#if defined(FEAT_PYTHON3) && defined(MSWIN)
+EXTERN char e_critical_error_in_python3_initialization_check_your_installation[]
+       INIT(= N_("E1266: Critical error in python3 initialization, check your python3 installation"));
+#endif
index c8ca3c37920c11f43401f9e350db46a18c65a73c..0b05857d5a3bad036af1543ddadf2e3d23b1bdfb 100644 (file)
@@ -112,12 +112,18 @@ typedef PyObject PySliceObject_T;
 typedef PySliceObject PySliceObject_T;
 #endif
 
+#ifndef MSWIN
+# define HINSTANCE void *
+#endif
+#if defined(DYNAMIC_PYTHON3) || defined(MSWIN)
+static HINSTANCE hinstPy3 = 0; // Instance of python.dll
+#endif
+
 #if defined(DYNAMIC_PYTHON3) || defined(PROTO)
 
 # ifndef MSWIN
 #  include <dlfcn.h>
 #  define FARPROC void*
-#  define HINSTANCE void*
 #  if defined(PY_NO_RTLD_GLOBAL) && defined(PY3_NO_RTLD_GLOBAL)
 #   define load_dll(n) dlopen((n), RTLD_LAZY)
 #  else
@@ -459,8 +465,6 @@ static void(*py3_PyObject_GC_Del)(void *);
 static void(*py3_PyObject_GC_UnTrack)(void *);
 static int (*py3_PyType_IsSubtype)(PyTypeObject *, PyTypeObject *);
 
-static HINSTANCE hinstPy3 = 0; // Instance of python.dll
-
 // Imported exception objects
 static PyObject *p3imp_PyExc_AttributeError;
 static PyObject *p3imp_PyExc_IndexError;
@@ -1032,13 +1036,8 @@ reset_stdin(void)
 {
     FILE *(*py__acrt_iob_func)(unsigned) = NULL;
     FILE *(*pyfreopen)(const char *, const char *, FILE *) = NULL;
-    HINSTANCE hinst;
+    HINSTANCE hinst = hinstPy3;
 
-# ifdef DYNAMIC_PYTHON3
-    hinst = hinstPy3;
-# else
-    hinst = GetModuleHandle(PYTHON3_DLL);
-# endif
     if (hinst == NULL || is_stdin_readable())
        return;
 
@@ -1061,6 +1060,57 @@ reset_stdin(void)
 }
 #else
 # define reset_stdin()
+#endif
+
+// Python 3.2 or later will abort inside Py_Initialize() when mandatory
+// modules cannot be loaded (e.g. 'pythonthreehome' is wrongly set.).
+// Install a hook to python dll's exit() and recover from it.
+#if defined(MSWIN) && (PY_VERSION_HEX >= 0x030200f0)
+# define HOOK_EXIT
+# include <setjmp.h>
+
+static jmp_buf exit_hook_jump_buf;
+static void *orig_exit = NULL;
+
+/*
+ * Function that replaces exit() while calling Py_Initialize().
+ */
+    static void
+hooked_exit(int ret)
+{
+    // Recover from exit.
+    longjmp(exit_hook_jump_buf, 1);
+}
+
+/*
+ * Install a hook to python dll's exit().
+ */
+    static void
+hook_py_exit(void)
+{
+    HINSTANCE hinst = hinstPy3;
+
+    if (hinst == NULL || orig_exit != NULL)
+       return;
+
+    orig_exit = hook_dll_import_func(hinst, "exit", (void *)hooked_exit);
+}
+
+/*
+ * Remove the hook installed by hook_py_exit().
+ */
+    static void
+restore_py_exit(void)
+{
+    HINSTANCE hinst = hinstPy3;
+
+    if (hinst == NULL)
+       return;
+
+    if (orig_exit != NULL)
+       hook_dll_import_func(hinst, "exit", orig_exit);
+    orig_exit = NULL;
+}
 #endif
 
     static int
@@ -1095,8 +1145,31 @@ Python3_Init(void)
 
        PyImport_AppendInittab("vim", Py3Init_vim);
 
+#if !defined(DYNAMIC_PYTHON3) && defined(MSWIN)
+       hinstPy3 = GetModuleHandle(PYTHON3_DLL);
+#endif
        reset_stdin();
-       Py_Initialize();
+
+#ifdef HOOK_EXIT
+       // Catch exit() called in Py_Initialize().
+       hook_py_exit();
+       if (setjmp(exit_hook_jump_buf) == 0)
+#endif
+       {
+           Py_Initialize();
+#ifdef HOOK_EXIT
+           restore_py_exit();
+#endif
+       }
+#ifdef HOOK_EXIT
+       else
+       {
+           // exit() was called in Py_Initialize().
+           restore_py_exit();
+           emsg(_(e_critical_error_in_python3_initialization_check_your_installation));
+           goto fail;
+       }
+#endif
 
 #if PY_VERSION_HEX < 0x03090000
        // Initialise threads.  This is deprecated since Python 3.9.
index a01cee5b812ed36ddf492fe9b9e7f974e0e38180..682fdf2bcf1b4e4ba099581387cd9b8cabcb12a0 100644 (file)
@@ -572,14 +572,18 @@ mch_is_gui_executable(void)
 }
 #endif
 
-#if defined(DYNAMIC_ICONV) || defined(DYNAMIC_GETTEXT) || defined(PROTO)
+#if defined(DYNAMIC_ICONV) || defined(DYNAMIC_GETTEXT) \
+    || defined(FEAT_PYTHON3) || defined(PROTO)
 /*
  * Get related information about 'funcname' which is imported by 'hInst'.
  * If 'info' is 0, return the function address.
  * If 'info' is 1, return the module name which the function is imported from.
+ * If 'info' is 2, hook the function with 'ptr', and return the original
+ * function address.
  */
     static void *
-get_imported_func_info(HINSTANCE hInst, const char *funcname, int info)
+get_imported_func_info(HINSTANCE hInst, const char *funcname, int info,
+       const void *ptr)
 {
     PBYTE                      pImage = (PBYTE)hInst;
     PIMAGE_DOS_HEADER          pDOS = (PIMAGE_DOS_HEADER)hInst;
@@ -611,12 +615,23 @@ get_imported_func_info(HINSTANCE hInst, const char *funcname, int info)
                                        + (UINT_PTR)(pINT->u1.AddressOfData));
            if (strcmp((char *)pImpName->Name, funcname) == 0)
            {
+               void *original;
+               DWORD old, new = PAGE_READWRITE;
+
                switch (info)
                {
                    case 0:
                        return (void *)pIAT->u1.Function;
                    case 1:
                        return (void *)(pImage + pImpDesc->Name);
+                   case 2:
+                       original = (void *)pIAT->u1.Function;
+                       VirtualProtect(&pIAT->u1.Function, sizeof(void *),
+                               new, &old);
+                       pIAT->u1.Function = (UINT_PTR)ptr;
+                       VirtualProtect(&pIAT->u1.Function, sizeof(void *),
+                               old, &new);
+                       return original;
                    default:
                        return NULL;
                }
@@ -634,7 +649,7 @@ find_imported_module_by_funcname(HINSTANCE hInst, const char *funcname)
 {
     char    *modulename;
 
-    modulename = (char *)get_imported_func_info(hInst, funcname, 1);
+    modulename = (char *)get_imported_func_info(hInst, funcname, 1, NULL);
     if (modulename != NULL)
        return GetModuleHandleA(modulename);
     return NULL;
@@ -646,7 +661,17 @@ find_imported_module_by_funcname(HINSTANCE hInst, const char *funcname)
     void *
 get_dll_import_func(HINSTANCE hInst, const char *funcname)
 {
-    return get_imported_func_info(hInst, funcname, 0);
+    return get_imported_func_info(hInst, funcname, 0, NULL);
+}
+
+/*
+ * Hook the function named 'funcname' which is imported by 'hInst' DLL,
+ * and return the original function address.
+ */
+    void *
+hook_dll_import_func(HINSTANCE hInst, const char *funcname, const void *hook)
+{
+    return get_imported_func_info(hInst, funcname, 2, hook);
 }
 #endif
 
index 5c50816c6f5b1c3289025c141c79359cc5404189..dac2b7142fb351b9440eb220715c75d6bcba53f9 100644 (file)
@@ -3,6 +3,7 @@ HINSTANCE vimLoadLib(char *name);
 int mch_is_gui_executable(void);
 HINSTANCE find_imported_module_by_funcname(HINSTANCE hInst, const char *funcname);
 void *get_dll_import_func(HINSTANCE hInst, const char *funcname);
+void *hook_dll_import_func(HINSTANCE hInst, const char *funcname, const void *hook);
 int dyn_libintl_init(void);
 void dyn_libintl_end(void);
 void PlatformId(void);
index 3caad5f83c37dfebf654453f6168553f86109e49..956df653a218311f0e4a665585bcc11c3046f93c 100644 (file)
@@ -746,6 +746,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    4317,
 /**/
     4316,
 /**/