(root)/
Python-3.12.0/
Python/
dynload_win.c
       1  
       2  /* Support for dynamic loading of extension modules */
       3  
       4  #include "Python.h"
       5  #include "pycore_fileutils.h"     // _Py_add_relfile()
       6  #include "pycore_pystate.h"       // _PyInterpreterState_GET()
       7  
       8  #ifdef HAVE_DIRECT_H
       9  #include <direct.h>
      10  #endif
      11  #include <ctype.h>
      12  
      13  #include "importdl.h"
      14  #include "patchlevel.h"
      15  #include <windows.h>
      16  
      17  #ifdef _DEBUG
      18  #define PYD_DEBUG_SUFFIX "_d"
      19  #else
      20  #define PYD_DEBUG_SUFFIX ""
      21  #endif
      22  
      23  #ifdef PYD_PLATFORM_TAG
      24  #define PYD_TAGGED_SUFFIX PYD_DEBUG_SUFFIX ".cp" Py_STRINGIFY(PY_MAJOR_VERSION) Py_STRINGIFY(PY_MINOR_VERSION) "-" PYD_PLATFORM_TAG ".pyd"
      25  #else
      26  #define PYD_TAGGED_SUFFIX PYD_DEBUG_SUFFIX ".cp" Py_STRINGIFY(PY_MAJOR_VERSION) Py_STRINGIFY(PY_MINOR_VERSION) ".pyd"
      27  #endif
      28  
      29  #define PYD_UNTAGGED_SUFFIX PYD_DEBUG_SUFFIX ".pyd"
      30  
      31  const char *_PyImport_DynLoadFiletab[] = {
      32      PYD_TAGGED_SUFFIX,
      33      PYD_UNTAGGED_SUFFIX,
      34      NULL
      35  };
      36  
      37  /* Function to return the name of the "python" DLL that the supplied module
      38     directly imports.  Looks through the list of imported modules and
      39     returns the first entry that starts with "python" (case sensitive) and
      40     is followed by nothing but numbers until the separator (period).
      41  
      42     Returns a pointer to the import name, or NULL if no matching name was
      43     located.
      44  
      45     This function parses through the PE header for the module as loaded in
      46     memory by the system loader.  The PE header is accessed as documented by
      47     Microsoft in the MSDN PE and COFF specification (2/99), and handles
      48     both PE32 and PE32+.  It only worries about the direct import table and
      49     not the delay load import table since it's unlikely an extension is
      50     going to be delay loading Python (after all, it's already loaded).
      51  
      52     If any magic values are not found (e.g., the PE header or optional
      53     header magic), then this function simply returns NULL. */
      54  
      55  #define DWORD_AT(mem) (*(DWORD *)(mem))
      56  #define WORD_AT(mem)  (*(WORD *)(mem))
      57  
      58  static char *GetPythonImport (HINSTANCE hModule)
      59  {
      60      unsigned char *dllbase, *import_data, *import_name;
      61      DWORD pe_offset, opt_offset;
      62      WORD opt_magic;
      63      int num_dict_off, import_off;
      64  
      65      /* Safety check input */
      66      if (hModule == NULL) {
      67          return NULL;
      68      }
      69  
      70      /* Module instance is also the base load address.  First portion of
      71         memory is the MS-DOS loader, which holds the offset to the PE
      72         header (from the load base) at 0x3C */
      73      dllbase = (unsigned char *)hModule;
      74      pe_offset = DWORD_AT(dllbase + 0x3C);
      75  
      76      /* The PE signature must be "PE\0\0" */
      77      if (memcmp(dllbase+pe_offset,"PE\0\0",4)) {
      78          return NULL;
      79      }
      80  
      81      /* Following the PE signature is the standard COFF header (20
      82         bytes) and then the optional header.  The optional header starts
      83         with a magic value of 0x10B for PE32 or 0x20B for PE32+ (PE32+
      84         uses 64-bits for some fields).  It might also be 0x107 for a ROM
      85         image, but we don't process that here.
      86  
      87         The optional header ends with a data dictionary that directly
      88         points to certain types of data, among them the import entries
      89         (in the second table entry). Based on the header type, we
      90         determine offsets for the data dictionary count and the entry
      91         within the dictionary pointing to the imports. */
      92  
      93      opt_offset = pe_offset + 4 + 20;
      94      opt_magic = WORD_AT(dllbase+opt_offset);
      95      if (opt_magic == 0x10B) {
      96          /* PE32 */
      97          num_dict_off = 92;
      98          import_off   = 104;
      99      } else if (opt_magic == 0x20B) {
     100          /* PE32+ */
     101          num_dict_off = 108;
     102          import_off   = 120;
     103      } else {
     104          /* Unsupported */
     105          return NULL;
     106      }
     107  
     108      /* Now if an import table exists, offset to it and walk the list of
     109         imports.  The import table is an array (ending when an entry has
     110         empty values) of structures (20 bytes each), which contains (at
     111         offset 12) a relative address (to the module base) at which a
     112         string constant holding the import name is located. */
     113  
     114      if (DWORD_AT(dllbase + opt_offset + num_dict_off) >= 2) {
     115          /* We have at least 2 tables - the import table is the second
     116             one.  But still it may be that the table size is zero */
     117          if (0 == DWORD_AT(dllbase + opt_offset + import_off + sizeof(DWORD)))
     118              return NULL;
     119          import_data = dllbase + DWORD_AT(dllbase +
     120                                           opt_offset +
     121                                           import_off);
     122          while (DWORD_AT(import_data)) {
     123              import_name = dllbase + DWORD_AT(import_data+12);
     124              if (strlen(import_name) >= 6 &&
     125                  !strncmp(import_name,"python",6)) {
     126                  char *pch;
     127  
     128                  /* Don't claim that python3.dll is a Python DLL. */
     129  #ifdef _DEBUG
     130                  if (strcmp(import_name, "python3_d.dll") == 0) {
     131  #else
     132                  if (strcmp(import_name, "python3.dll") == 0) {
     133  #endif
     134                      import_data += 20;
     135                      continue;
     136                  }
     137  
     138                  /* Ensure python prefix is followed only
     139                     by numbers to the end of the basename */
     140                  pch = import_name + 6;
     141  #ifdef _DEBUG
     142                  while (*pch && pch[0] != '_' && pch[1] != 'd' && pch[2] != '.') {
     143  #else
     144                  while (*pch && *pch != '.') {
     145  #endif
     146                      if (*pch >= '0' && *pch <= '9') {
     147                          pch++;
     148                      } else {
     149                          pch = NULL;
     150                          break;
     151                      }
     152                  }
     153  
     154                  if (pch) {
     155                      /* Found it - return the name */
     156                      return import_name;
     157                  }
     158              }
     159              import_data += 20;
     160          }
     161      }
     162  
     163      return NULL;
     164  }
     165  
     166  #ifdef Py_ENABLE_SHARED
     167  /* Load python3.dll before loading any extension module that might refer
     168     to it. That way, we can be sure that always the python3.dll corresponding
     169     to this python DLL is loaded, not a python3.dll that might be on the path
     170     by chance.
     171     Return whether the DLL was found.
     172  */
     173  extern HMODULE PyWin_DLLhModule;
     174  static int
     175  _Py_CheckPython3(void)
     176  {
     177      static int python3_checked = 0;
     178      static HANDLE hPython3;
     179      #define MAXPATHLEN 512
     180      wchar_t py3path[MAXPATHLEN+1];
     181      if (python3_checked) {
     182          return hPython3 != NULL;
     183      }
     184      python3_checked = 1;
     185  
     186      /* If there is a python3.dll next to the python3y.dll,
     187         use that DLL */
     188      if (PyWin_DLLhModule && GetModuleFileNameW(PyWin_DLLhModule, py3path, MAXPATHLEN)) {
     189          wchar_t *p = wcsrchr(py3path, L'\\');
     190          if (p) {
     191              wcscpy(p + 1, PY3_DLLNAME);
     192              hPython3 = LoadLibraryExW(py3path, NULL, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
     193              if (hPython3 != NULL) {
     194                  return 1;
     195              }
     196          }
     197      }
     198  
     199      /* If we can locate python3.dll in our application dir,
     200         use that DLL */
     201      hPython3 = LoadLibraryExW(PY3_DLLNAME, NULL, LOAD_LIBRARY_SEARCH_APPLICATION_DIR);
     202      if (hPython3 != NULL) {
     203          return 1;
     204      }
     205  
     206      /* For back-compat, also search {sys.prefix}\DLLs, though
     207         that has not been a normal install layout for a while */
     208      PyInterpreterState *interp = _PyInterpreterState_GET();
     209      PyConfig *config = (PyConfig*)_PyInterpreterState_GetConfig(interp);
     210      assert(config->prefix);
     211      if (config->prefix) {
     212          wcscpy_s(py3path, MAXPATHLEN, config->prefix);
     213          if (py3path[0] && _Py_add_relfile(py3path, L"DLLs\\" PY3_DLLNAME, MAXPATHLEN) >= 0) {
     214              hPython3 = LoadLibraryExW(py3path, NULL, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
     215          }
     216      }
     217      return hPython3 != NULL;
     218      #undef MAXPATHLEN
     219  }
     220  #endif /* Py_ENABLE_SHARED */
     221  
     222  dl_funcptr _PyImport_FindSharedFuncptrWindows(const char *prefix,
     223                                                const char *shortname,
     224                                                PyObject *pathname, FILE *fp)
     225  {
     226      dl_funcptr p;
     227      char funcname[258], *import_python;
     228  
     229  #ifdef Py_ENABLE_SHARED
     230      _Py_CheckPython3();
     231  #endif /* Py_ENABLE_SHARED */
     232  
     233      wchar_t *wpathname = PyUnicode_AsWideCharString(pathname, NULL);
     234      if (wpathname == NULL)
     235          return NULL;
     236  
     237      PyOS_snprintf(funcname, sizeof(funcname), "%.20s_%.200s", prefix, shortname);
     238  
     239      {
     240          HINSTANCE hDLL = NULL;
     241  #ifdef MS_WINDOWS_DESKTOP
     242          unsigned int old_mode;
     243  
     244          /* Don't display a message box when Python can't load a DLL */
     245          old_mode = SetErrorMode(SEM_FAILCRITICALERRORS);
     246  #endif
     247  
     248          /* bpo-36085: We use LoadLibraryEx with restricted search paths
     249             to avoid DLL preloading attacks and enable use of the
     250             AddDllDirectory function. We add SEARCH_DLL_LOAD_DIR to
     251             ensure DLLs adjacent to the PYD are preferred. */
     252          Py_BEGIN_ALLOW_THREADS
     253          hDLL = LoadLibraryExW(wpathname, NULL,
     254                                LOAD_LIBRARY_SEARCH_DEFAULT_DIRS |
     255                                LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR);
     256          Py_END_ALLOW_THREADS
     257          PyMem_Free(wpathname);
     258  
     259  #ifdef MS_WINDOWS_DESKTOP
     260          /* restore old error mode settings */
     261          SetErrorMode(old_mode);
     262  #endif
     263  
     264          if (hDLL==NULL){
     265              PyObject *message;
     266              unsigned int errorCode;
     267  
     268              /* Get an error string from Win32 error code */
     269              wchar_t theInfo[256]; /* Pointer to error text
     270                                    from system */
     271              int theLength; /* Length of error text */
     272  
     273              errorCode = GetLastError();
     274  
     275              theLength = FormatMessageW(
     276                  FORMAT_MESSAGE_FROM_SYSTEM |
     277                  FORMAT_MESSAGE_IGNORE_INSERTS, /* flags */
     278                  NULL, /* message source */
     279                  errorCode, /* the message (error) ID */
     280                  MAKELANGID(LANG_NEUTRAL,
     281                             SUBLANG_DEFAULT),
     282                             /* Default language */
     283                  theInfo, /* the buffer */
     284                  sizeof(theInfo) / sizeof(wchar_t), /* size in wchars */
     285                  NULL); /* no additional format args. */
     286  
     287              /* Problem: could not get the error message.
     288                 This should not happen if called correctly. */
     289              if (theLength == 0) {
     290                  message = PyUnicode_FromFormat(
     291                      "DLL load failed with error code %u while importing %s",
     292                      errorCode, shortname);
     293              } else {
     294                  /* For some reason a \r\n
     295                     is appended to the text */
     296                  if (theLength >= 2 &&
     297                      theInfo[theLength-2] == '\r' &&
     298                      theInfo[theLength-1] == '\n') {
     299                      theLength -= 2;
     300                      theInfo[theLength] = '\0';
     301                  }
     302                  message = PyUnicode_FromFormat(
     303                      "DLL load failed while importing %s: ", shortname);
     304  
     305                  PyUnicode_AppendAndDel(&message,
     306                      PyUnicode_FromWideChar(
     307                          theInfo,
     308                          theLength));
     309              }
     310              if (message != NULL) {
     311                  PyObject *shortname_obj = PyUnicode_FromString(shortname);
     312                  PyErr_SetImportError(message, shortname_obj, pathname);
     313                  Py_XDECREF(shortname_obj);
     314                  Py_DECREF(message);
     315              }
     316              return NULL;
     317          } else {
     318              char buffer[256];
     319  
     320              PyOS_snprintf(buffer, sizeof(buffer),
     321  #ifdef _DEBUG
     322                            "python%d%d_d.dll",
     323  #else
     324                            "python%d%d.dll",
     325  #endif
     326                            PY_MAJOR_VERSION,PY_MINOR_VERSION);
     327              import_python = GetPythonImport(hDLL);
     328  
     329              if (import_python &&
     330                  _stricmp(buffer,import_python)) {
     331                  PyErr_Format(PyExc_ImportError,
     332                               "Module use of %.150s conflicts "
     333                               "with this version of Python.",
     334                               import_python);
     335                  Py_BEGIN_ALLOW_THREADS
     336                  FreeLibrary(hDLL);
     337                  Py_END_ALLOW_THREADS
     338                  return NULL;
     339              }
     340          }
     341          Py_BEGIN_ALLOW_THREADS
     342          p = GetProcAddress(hDLL, funcname);
     343          Py_END_ALLOW_THREADS
     344      }
     345  
     346      return p;
     347  }