GNU Octave  9.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
oct-shlib.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 1999-2024 The Octave Project Developers
4 //
5 // See the file COPYRIGHT.md in the top-level directory of this
6 // distribution or <https://octave.org/copyright/>.
7 //
8 // This file is part of Octave.
9 //
10 // Octave is free software: you can redistribute it and/or modify it
11 // under the terms of the GNU General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // Octave is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with Octave; see the file COPYING. If not, see
22 // <https://www.gnu.org/licenses/>.
23 //
24 ////////////////////////////////////////////////////////////////////////
25 
26 #if defined (HAVE_CONFIG_H)
27 # include "config.h"
28 #endif
29 
30 #include <list>
31 #include <map>
32 
33 extern "C"
34 {
35 #if defined (HAVE_DLOPEN_API)
36 # if defined (HAVE_DLFCN_H)
37 # include <dlfcn.h>
38 # else
39  extern void * dlopen (const char *, int);
40  extern const char * dlerror (void);
41  extern void * dlsym (void *, const char *);
42  extern int dlclose (void *);
43 # endif
44 #elif defined (HAVE_LOADLIBRARY_API)
45 # define WIN32_LEAN_AND_MEAN 1
46 # include <windows.h>
47 # include <psapi.h>
48 #endif
49 }
50 
51 #include "file-ops.h"
52 #include "file-stat.h"
53 #include "lo-error.h"
54 #include "oct-shlib.h"
55 #include "str-vec.h"
56 
57 #if defined (HAVE_LOADLIBRARY_API)
58 # include "lo-sysdep.h"
59 #endif
60 
62 
64 
65 void
66 dynamic_library::delete_later ()
67 {
69 }
70 
71 int
73 {
75 
76  return 0;
77 }
78 
80  : m_count (1), m_fcn_names (), m_file (f), m_time_loaded (),
81  m_search_all_loaded (false)
82 {
83  s_instances[f] = this;
84 
85  if (is_out_of_date ())
86  (*current_liboctave_warning_with_id_handler)
87  ("Octave:future-time-stamp",
88  "time stamp for file '%s' is in the future", m_file.c_str ());
89 }
90 
91 bool
93 {
94  sys::file_stat fs (m_file);
95  return (fs && fs.is_newer (m_time_loaded));
96 }
97 
98 void
100 {
101  // We can't actually reload the library, but we'll pretend we did.
102  sys::file_stat fs (m_file);
103  if (fs && fs.is_newer (m_time_loaded))
104  {
105  m_time_loaded = fs.mtime ();
106 
107  (*current_liboctave_warning_with_id_handler)
108  ("Octave:library-reload",
109  "library %s not reloaded due to existing references", m_file.c_str ());
110  }
111 }
112 
114 dynamic_library::dynlib_rep::get_instance (const std::string& f, bool fake)
115 {
116  dynlib_rep *retval = nullptr;
117  std::map<std::string, dynlib_rep *>::iterator p = s_instances.find (f);
118  if (p != s_instances.end ())
119  {
120  retval = p->second;
121  retval->m_count++;
122  if (fake)
123  retval->fake_reload ();
124  }
125  else
126  retval = new_instance (f);
127 
128  return retval;
129 }
130 
131 std::list<std::string>
133 {
134  std::list<std::string> retval;
135 
136  for (const auto& p : m_fcn_names)
137  retval.push_back (p.first);
138 
139  return retval;
140 }
141 
142 void
144 {
145  auto p = m_fcn_names.find (name);
146 
147  if (p == m_fcn_names.end ())
148  m_fcn_names[name] = 1;
149  else
150  ++(p->second);
151 }
152 
153 bool
154 dynamic_library::dynlib_rep::remove_fcn_name (const std::string& fcn_name)
155 {
156  bool retval = false;
157 
158  auto p = m_fcn_names.find (fcn_name);
159 
160  if (p != m_fcn_names.end () && --(p->second) == 0)
161  {
162  m_fcn_names.erase (fcn_name);
163  retval = true;
164  }
165 
166  return retval;
167 }
168 
169 std::map<std::string, dynamic_library::dynlib_rep *>
171 
172 dynamic_library::dynlib_rep dynamic_library::s_nil_rep;
173 
174 #if defined (HAVE_DLOPEN_API)
175 
176 class
177 octave_dlopen_shlib : public dynamic_library::dynlib_rep
178 {
179 public:
180 
181  octave_dlopen_shlib (const std::string& f);
182 
183  OCTAVE_DISABLE_CONSTRUCT_COPY_MOVE (octave_dlopen_shlib)
184 
185  ~octave_dlopen_shlib ();
186 
187  void * search (const std::string& name,
188  const dynamic_library::name_mangler& mangler
190 
191  // FIXME: this is possibly redundant because failure to open a library will
192  // normally throw an exception, avoiding the construction of an invalid
193  // library. Leave it here for possible future use.
194 
195  bool is_open () const
196  {
197  return (m_search_all_loaded || m_library != nullptr);
198  }
199 
200 private:
201 
202  void *m_library;
203 };
204 
205 octave_dlopen_shlib::octave_dlopen_shlib (const std::string& f)
206  : dynamic_library::dynlib_rep (f), m_library (nullptr)
207 {
208  int flags = 0;
209 
210  // Use RTLD_NOW to resolve all symbols before dlopen returns.
211  // By using this option, dlopen will detect errors and Octave
212  // won't exit if there are unresolved symbols in the file we are
213  // loading, and we may even get a useful diagnostic.
214 # if defined (RTLD_NOW)
215  flags |= RTLD_NOW;
216 # endif
217 
218  // Use RTLD_GLOBAL to export symbols from loaded objects so they are
219  // available to other subsequently loaded libraries.
220 # if defined (RTLD_GLOBAL)
221  flags |= RTLD_GLOBAL;
222 # endif
223 
224  if (m_file.empty ())
225  {
226  m_search_all_loaded = true;
227  return;
228  }
229 
230  m_library = dlopen (m_file.c_str (), flags);
231 
232  if (! m_library)
233  {
234  const char *msg = dlerror ();
235 
236  if (msg)
237  (*current_liboctave_error_handler)
238  ("%s: failed to load\nIncompatible version or missing dependency?"
239  "\n%s", m_file.c_str (), msg);
240  else
242  ("%s: failed to load\nIncompatible version or missing dependency?",
243  m_file.c_str ());
244  }
245 }
246 
247 octave_dlopen_shlib::~octave_dlopen_shlib ()
248 {
249  if (m_library)
250  dlclose (m_library);
251 }
252 
253 void *
254 octave_dlopen_shlib::search (const std::string& name,
255  const dynamic_library::name_mangler& mangler)
256 {
257  void *function = nullptr;
258 
259  if (! is_open ())
260  (*current_liboctave_error_handler)
261  ("shared library %s is not open", m_file.c_str ());
262 
263  std::string sym_name = name;
264 
265  if (mangler)
266  sym_name = mangler (name);
267 
268  if (m_search_all_loaded)
269  function = dlsym (RTLD_DEFAULT, sym_name.c_str ());
270  else
271  function = dlsym (m_library, sym_name.c_str ());
272 
273  return function;
274 }
275 
276 #elif defined (HAVE_LOADLIBRARY_API)
277 
278 class
279 octave_w32_shlib: public dynamic_library::dynlib_rep
280 {
281 public:
282 
283  octave_w32_shlib (const std::string& f);
284 
285  OCTAVE_DISABLE_COPY_MOVE (octave_w32_shlib)
286 
287  ~octave_w32_shlib ();
288 
289  void * search (const std::string& name,
290  const dynamic_library::name_mangler& mangler
292 
293  void * global_search (const std::string& sym_name);
294 
295  bool is_open () const
296  {
297  return (m_search_all_loaded || m_handle != nullptr);
298  }
299 
300 private:
301 
302  HINSTANCE m_handle;
303 };
304 
305 octave_w32_shlib::octave_w32_shlib (const std::string& f)
306  : dynamic_library::dynlib_rep (f), m_handle (nullptr)
307 {
308  if (f.empty())
309  {
310  m_search_all_loaded = true;
311  return;
312  }
313 
314  std::string dir = sys::file_ops::dirname (f);
315  std::wstring wdir = sys::u8_to_wstring (dir);
316  SetDllDirectoryW (dir.empty ()
317  ? nullptr : wdir.c_str ());
318 
319  std::wstring wfile = sys::u8_to_wstring (m_file);
320  m_handle = LoadLibraryW (wfile.c_str ());
321 
322  SetDllDirectoryW (nullptr);
323 
324  if (! m_handle)
325  {
326  DWORD last_error = GetLastError ();
327 
328  wchar_t *error_text = nullptr;
329  FormatMessageW (FORMAT_MESSAGE_FROM_SYSTEM |
330  FORMAT_MESSAGE_ALLOCATE_BUFFER |
331  FORMAT_MESSAGE_IGNORE_INSERTS,
332  nullptr, last_error,
333  MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
334  reinterpret_cast <wchar_t *> (&error_text), 0, nullptr);
335 
336  std::ostringstream err_str;
337  err_str << "opening the library '" << m_file << "' failed (error "
338  << last_error << "): ";
339  if (error_text != nullptr)
340  {
341  err_str << sys::u8_from_wstring (error_text);
342  LocalFree (error_text);
343  }
344  else
345  err_str << "Unknown error.";
346 
347  (*current_liboctave_error_handler) ("%s", err_str.str ().c_str ());
348  }
349 }
350 
351 octave_w32_shlib::~octave_w32_shlib ()
352 {
353  if (m_handle)
354  FreeLibrary (m_handle);
355 }
356 
357 void *
358 octave_w32_shlib::global_search (const std::string& sym_name)
359 {
360  void *function = nullptr;
361 
362  HANDLE proc = GetCurrentProcess ();
363 
364  if (! proc)
365  (*current_liboctave_error_handler)
366  ("Unable to get handle to own process.");
367 
368  std::size_t lib_num = 64;
369  std::size_t size_lib = sizeof (HMODULE);
370  HMODULE *h_libs;
371  DWORD bytes_all_libs;
372  bool got_libs;
373 
374  // Get a list of all the libraries in own process.
375  h_libs = static_cast<HMODULE *> (malloc (size_lib*lib_num));
376  got_libs = EnumProcessModules (proc, h_libs, size_lib*lib_num,
377  &bytes_all_libs);
378  int ii = 0;
379  while (((size_lib*lib_num) < bytes_all_libs) && ii++ < 3)
380  {
381  lib_num = bytes_all_libs / size_lib;
382  h_libs = static_cast<HMODULE *> (realloc (h_libs, bytes_all_libs));
383  got_libs = EnumProcessModules (proc, h_libs, bytes_all_libs,
384  &bytes_all_libs);
385  }
386 
387  if (got_libs)
388  {
389  for (std::size_t i = 0; i < (bytes_all_libs / size_lib); i++)
390  {
391  // Check for function in library.
392  function = reinterpret_cast<void *>
393  (GetProcAddress (h_libs[i], sym_name.c_str ()));
394 
395  if (function)
396  break;
397  }
398  }
399 
400  // Release the handle to the process.
401  CloseHandle (proc);
402 
403  return function;
404 }
405 
406 void *
407 octave_w32_shlib::search (const std::string& name,
408  const dynamic_library::name_mangler& mangler)
409 {
410  void *function = nullptr;
411 
412  if (! m_search_all_loaded && ! is_open ())
413  (*current_liboctave_error_handler)
414  ("shared library %s is not open", m_file.c_str ());
415 
416  std::string sym_name = name;
417 
418  if (mangler)
419  sym_name = mangler (name);
420 
421  if (m_search_all_loaded)
422  function = global_search (sym_name);
423  else
424  function = reinterpret_cast<void *> (GetProcAddress (m_handle,
425  sym_name.c_str ()));
426 
427  return function;
428 }
429 
430 #endif
431 
434 {
435 #if defined (HAVE_DLOPEN_API)
436  return new octave_dlopen_shlib (f);
437 #elif defined (HAVE_LOADLIBRARY_API)
438  return new octave_w32_shlib (f);
439 #else
440  (*current_liboctave_error_handler)
441  ("support for dynamically loaded libraries was unavailable or disabled when liboctave was built");
442 #endif
443 }
444 
445 OCTAVE_END_NAMESPACE(octave)
bool remove_fcn_name(const std::string &)
Definition: oct-shlib.cc:154
static std::map< std::string, dynlib_rep * > s_instances
Definition: oct-shlib.h:109
void add_fcn_name(const std::string &)
Definition: oct-shlib.cc:143
static dynlib_rep * new_instance(const std::string &f)
Definition: oct-shlib.cc:433
bool is_out_of_date() const
Definition: oct-shlib.cc:92
static dynlib_rep * get_instance(const std::string &f, bool fake)
Definition: oct-shlib.cc:114
std::list< std::string > function_names() const
Definition: oct-shlib.cc:132
refcount< octave_idx_type > m_count
Definition: oct-shlib.h:103
void * search(const std::string &nm, const name_mangler &mangler=name_mangler()) const
Definition: oct-shlib.h:179
std::function< std::string(const std::string &)> name_mangler
Definition: oct-shlib.h:46
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn
std::string dirname(const std::string &path)
OCTAVE_NORETURN liboctave_error_handler current_liboctave_error_handler
Definition: lo-error.c:41
F77_RET_T const F77_DBLE const F77_DBLE * f
std::string u8_from_wstring(const std::wstring &wchar_string)
Definition: lo-sysdep.cc:746
std::wstring u8_to_wstring(const std::string &utf8_string)
Definition: lo-sysdep.cc:723
int release_unreferenced_dynamic_libraries()
Definition: oct-shlib.cc:72
std::list< dynamic_library > possibly_unreferenced_dynamic_libraries
Definition: oct-shlib.cc:63
void * malloc(unsigned)