(root)/
Python-3.11.7/
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  /* Load python3.dll before loading any extension module that might refer
     167     to it. That way, we can be sure that always the python3.dll corresponding
     168     to this python DLL is loaded, not a python3.dll that might be on the path
     169     by chance.
     170     Return whether the DLL was found.
     171  */
     172  extern HMODULE PyWin_DLLhModule;
     173  static int
     174  _Py_CheckPython3(void)
     175  {
     176      static int python3_checked = 0;
     177      static HANDLE hPython3;
     178      #define MAXPATHLEN 512
     179      wchar_t py3path[MAXPATHLEN+1];
     180      if (python3_checked) {
     181          return hPython3 != NULL;
     182      }
     183      python3_checked = 1;
     184  
     185      /* If there is a python3.dll next to the python3y.dll,
     186         use that DLL */
     187      if (PyWin_DLLhModule && GetModuleFileNameW(PyWin_DLLhModule, py3path, MAXPATHLEN)) {
     188          wchar_t *p = wcsrchr(py3path, L'\\');
     189          if (p) {
     190              wcscpy(p + 1, PY3_DLLNAME);
     191              hPython3 = LoadLibraryExW(py3path, NULL, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
     192              if (hPython3 != NULL) {
     193                  return 1;
     194              }
     195          }
     196      }
     197  
     198      /* If we can locate python3.dll in our application dir,
     199         use that DLL */
     200      hPython3 = LoadLibraryExW(PY3_DLLNAME, NULL, LOAD_LIBRARY_SEARCH_APPLICATION_DIR);
     201      if (hPython3 != NULL) {
     202          return 1;
     203      }
     204  
     205      /* For back-compat, also search {sys.prefix}\DLLs, though
     206         that has not been a normal install layout for a while */
     207      PyInterpreterState *interp = _PyInterpreterState_GET();
     208      PyConfig *config = (PyConfig*)_PyInterpreterState_GetConfig(interp);
     209      assert(config->prefix);
     210      if (config->prefix) {
     211          wcscpy_s(py3path, MAXPATHLEN, config->prefix);
     212          if (py3path[0] && _Py_add_relfile(py3path, L"DLLs\\" PY3_DLLNAME, MAXPATHLEN) >= 0) {
     213              hPython3 = LoadLibraryExW(py3path, NULL, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
     214          }
     215      }
     216      return hPython3 != NULL;
     217      #undef MAXPATHLEN
     218  }
     219  
     220  dl_funcptr _PyImport_FindSharedFuncptrWindows(const char *prefix,
     221                                                const char *shortname,
     222                                                PyObject *pathname, FILE *fp)
     223  {
     224      dl_funcptr p;
     225      char funcname[258], *import_python;
     226  
     227      _Py_CheckPython3();
     228  
     229  #if USE_UNICODE_WCHAR_CACHE
     230      const wchar_t *wpathname = _PyUnicode_AsUnicode(pathname);
     231  #else /* USE_UNICODE_WCHAR_CACHE */
     232      wchar_t *wpathname = PyUnicode_AsWideCharString(pathname, NULL);
     233  #endif /* USE_UNICODE_WCHAR_CACHE */
     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          unsigned int old_mode;
     242  
     243          /* Don't display a message box when Python can't load a DLL */
     244          old_mode = SetErrorMode(SEM_FAILCRITICALERRORS);
     245  
     246          /* bpo-36085: We use LoadLibraryEx with restricted search paths
     247             to avoid DLL preloading attacks and enable use of the
     248             AddDllDirectory function. We add SEARCH_DLL_LOAD_DIR to
     249             ensure DLLs adjacent to the PYD are preferred. */
     250          Py_BEGIN_ALLOW_THREADS
     251          hDLL = LoadLibraryExW(wpathname, NULL,
     252                                LOAD_LIBRARY_SEARCH_DEFAULT_DIRS |
     253                                LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR);
     254          Py_END_ALLOW_THREADS
     255  #if !USE_UNICODE_WCHAR_CACHE
     256          PyMem_Free(wpathname);
     257  #endif /* USE_UNICODE_WCHAR_CACHE */
     258  
     259          /* restore old error mode settings */
     260          SetErrorMode(old_mode);
     261  
     262          if (hDLL==NULL){
     263              PyObject *message;
     264              unsigned int errorCode;
     265  
     266              /* Get an error string from Win32 error code */
     267              wchar_t theInfo[256]; /* Pointer to error text
     268                                    from system */
     269              int theLength; /* Length of error text */
     270  
     271              errorCode = GetLastError();
     272  
     273              theLength = FormatMessageW(
     274                  FORMAT_MESSAGE_FROM_SYSTEM |
     275                  FORMAT_MESSAGE_IGNORE_INSERTS, /* flags */
     276                  NULL, /* message source */
     277                  errorCode, /* the message (error) ID */
     278                  MAKELANGID(LANG_NEUTRAL,
     279                             SUBLANG_DEFAULT),
     280                             /* Default language */
     281                  theInfo, /* the buffer */
     282                  sizeof(theInfo) / sizeof(wchar_t), /* size in wchars */
     283                  NULL); /* no additional format args. */
     284  
     285              /* Problem: could not get the error message.
     286                 This should not happen if called correctly. */
     287              if (theLength == 0) {
     288                  message = PyUnicode_FromFormat(
     289                      "DLL load failed with error code %u while importing %s",
     290                      errorCode, shortname);
     291              } else {
     292                  /* For some reason a \r\n
     293                     is appended to the text */
     294                  if (theLength >= 2 &&
     295                      theInfo[theLength-2] == '\r' &&
     296                      theInfo[theLength-1] == '\n') {
     297                      theLength -= 2;
     298                      theInfo[theLength] = '\0';
     299                  }
     300                  message = PyUnicode_FromFormat(
     301                      "DLL load failed while importing %s: ", shortname);
     302  
     303                  PyUnicode_AppendAndDel(&message,
     304                      PyUnicode_FromWideChar(
     305                          theInfo,
     306                          theLength));
     307              }
     308              if (message != NULL) {
     309                  PyObject *shortname_obj = PyUnicode_FromString(shortname);
     310                  PyErr_SetImportError(message, shortname_obj, pathname);
     311                  Py_XDECREF(shortname_obj);
     312                  Py_DECREF(message);
     313              }
     314              return NULL;
     315          } else {
     316              char buffer[256];
     317  
     318              PyOS_snprintf(buffer, sizeof(buffer),
     319  #ifdef _DEBUG
     320                            "python%d%d_d.dll",
     321  #else
     322                            "python%d%d.dll",
     323  #endif
     324                            PY_MAJOR_VERSION,PY_MINOR_VERSION);
     325              import_python = GetPythonImport(hDLL);
     326  
     327              if (import_python &&
     328                  _stricmp(buffer,import_python)) {
     329                  PyErr_Format(PyExc_ImportError,
     330                               "Module use of %.150s conflicts "
     331                               "with this version of Python.",
     332                               import_python);
     333                  Py_BEGIN_ALLOW_THREADS
     334                  FreeLibrary(hDLL);
     335                  Py_END_ALLOW_THREADS
     336                  return NULL;
     337              }
     338          }
     339          Py_BEGIN_ALLOW_THREADS
     340          p = GetProcAddress(hDLL, funcname);
     341          Py_END_ALLOW_THREADS
     342      }
     343  
     344      return p;
     345  }