GNU Octave  8.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-2023 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 dynamic_library::delete_later (void)
66 {
68 }
69 
71 {
73 
74  return 0;
75 }
76 
78  : m_count (1), m_fcn_names (), m_file (f), m_time_loaded (),
79  m_search_all_loaded (false)
80 {
81  s_instances[f] = this;
82 
83  if (is_out_of_date ())
84  (*current_liboctave_warning_with_id_handler)
85  ("Octave:warn-future-time-stamp",
86  "timestamp on file %s is in the future", m_file.c_str ());
87 }
88 
89 bool
91 {
92  sys::file_stat fs (m_file);
93  return (fs && fs.is_newer (m_time_loaded));
94 }
95 
96 void
98 {
99  // We can't actually reload the library, but we'll pretend we did.
100  sys::file_stat fs (m_file);
101  if (fs && fs.is_newer (m_time_loaded))
102  {
103  m_time_loaded = fs.mtime ();
104 
105  (*current_liboctave_warning_with_id_handler)
106  ("Octave:library-reload",
107  "library %s not reloaded due to existing references", m_file.c_str ());
108  }
109 }
110 
112 dynamic_library::dynlib_rep::get_instance (const std::string& f, bool fake)
113 {
114  dynlib_rep *retval = nullptr;
115  std::map<std::string, dynlib_rep *>::iterator p = s_instances.find (f);
116  if (p != s_instances.end ())
117  {
118  retval = p->second;
119  retval->m_count++;
120  if (fake)
121  retval->fake_reload ();
122  }
123  else
124  retval = new_instance (f);
125 
126  return retval;
127 }
128 
129 std::list<std::string>
131 {
132  std::list<std::string> retval;
133 
134  for (const auto& p : m_fcn_names)
135  retval.push_back (p.first);
136 
137  return retval;
138 }
139 
140 void
142 {
143  auto p = m_fcn_names.find (name);
144 
145  if (p == m_fcn_names.end ())
146  m_fcn_names[name] = 1;
147  else
148  ++(p->second);
149 }
150 
151 bool
152 dynamic_library::dynlib_rep::remove_fcn_name (const std::string& fcn_name)
153 {
154  bool retval = false;
155 
156  auto p = m_fcn_names.find (fcn_name);
157 
158  if (p != m_fcn_names.end () && --(p->second) == 0)
159  {
160  m_fcn_names.erase (fcn_name);
161  retval = true;
162  }
163 
164  return retval;
165 }
166 
167 std::map<std::string, dynamic_library::dynlib_rep *>
169 
171 
172 #if defined (HAVE_DLOPEN_API)
173 
174 class
175 octave_dlopen_shlib : public dynamic_library::dynlib_rep
176 {
177 public:
178 
179  octave_dlopen_shlib (const std::string& f);
180 
181  // No copying!
182 
183  octave_dlopen_shlib (const octave_dlopen_shlib&) = delete;
184 
185  octave_dlopen_shlib& operator = (const octave_dlopen_shlib&) = delete;
186 
187  ~octave_dlopen_shlib (void);
188 
189  void * search (const std::string& name,
190  const dynamic_library::name_mangler& mangler
192 
193  // FIXME: this is possibly redundant because failure to open a library will
194  // normally throw an exception, avoiding the construction of an invalid
195  // library. Leave it here for possible future use.
196 
197  bool is_open (void) const
198  {
199  return (m_search_all_loaded || m_library != nullptr);
200  }
201 
202 private:
203 
204  void *m_library;
205 };
206 
207 octave_dlopen_shlib::octave_dlopen_shlib (const std::string& f)
208  : dynamic_library::dynlib_rep (f), m_library (nullptr)
209 {
210  int flags = 0;
211 
212  // Use RTLD_NOW to resolve all symbols before dlopen returns.
213  // By using this option, dlopen will detect errors and Octave
214  // won't exit if there are unresolved symbols in the file we are
215  // loading, and we may even get a useful diagnostic.
216 # if defined (RTLD_NOW)
217  flags |= RTLD_NOW;
218 # endif
219 
220  // Use RTLD_GLOBAL to export symbols from loaded objects so they are
221  // available to other subsequently loaded libraries.
222 # if defined (RTLD_GLOBAL)
223  flags |= RTLD_GLOBAL;
224 # endif
225 
226  if (m_file.empty ())
227  {
228  m_search_all_loaded = true;
229  return;
230  }
231 
232  m_library = dlopen (m_file.c_str (), flags);
233 
234  if (! m_library)
235  {
236  const char *msg = dlerror ();
237 
238  if (msg)
239  (*current_liboctave_error_handler)
240  ("%s: failed to load\nIncompatible version or missing dependency?"
241  "\n%s", m_file.c_str (), msg);
242  else
244  ("%s: failed to load\nIncompatible version or missing dependency?",
245  m_file.c_str ());
246  }
247 }
248 
249 octave_dlopen_shlib::~octave_dlopen_shlib (void)
250 {
251  if (m_library)
252  dlclose (m_library);
253 }
254 
255 void *
256 octave_dlopen_shlib::search (const std::string& name,
257  const dynamic_library::name_mangler& mangler)
258 {
259  void *function = nullptr;
260 
261  if (! is_open ())
262  (*current_liboctave_error_handler)
263  ("shared library %s is not open", m_file.c_str ());
264 
265  std::string sym_name = name;
266 
267  if (mangler)
268  sym_name = mangler (name);
269 
270  if (m_search_all_loaded)
271  function = dlsym (RTLD_DEFAULT, sym_name.c_str ());
272  else
273  function = dlsym (m_library, sym_name.c_str ());
274 
275  return function;
276 }
277 
278 #elif defined (HAVE_LOADLIBRARY_API)
279 
280 class
281 octave_w32_shlib: public dynamic_library::dynlib_rep
282 {
283 public:
284 
285  octave_w32_shlib (const std::string& f);
286 
287  // No copying!
288 
289  octave_w32_shlib (const octave_w32_shlib&) = delete;
290 
291  octave_w32_shlib& operator = (const octave_w32_shlib&) = delete;
292 
293  ~octave_w32_shlib (void);
294 
295  void * search (const std::string& name,
296  const dynamic_library::name_mangler& mangler
298 
299  void * global_search (const std::string& sym_name);
300 
301  bool is_open (void) const
302  {
303  return (m_search_all_loaded || m_handle != nullptr);
304  }
305 
306 private:
307 
308  HINSTANCE m_handle;
309 };
310 
311 octave_w32_shlib::octave_w32_shlib (const std::string& f)
312  : dynamic_library::dynlib_rep (f), m_handle (nullptr)
313 {
314  if (f.empty())
315  {
316  m_search_all_loaded = true;
317  return;
318  }
319 
320  std::string dir = sys::file_ops::dirname (f);
321  std::wstring wdir = sys::u8_to_wstring (dir);
322  SetDllDirectoryW (dir.empty ()
323  ? nullptr : wdir.c_str ());
324 
325  std::wstring wfile = sys::u8_to_wstring (m_file);
326  m_handle = LoadLibraryW (wfile.c_str ());
327 
328  SetDllDirectoryW (nullptr);
329 
330  if (! m_handle)
331  {
332  DWORD last_error = GetLastError ();
333 
334  wchar_t *error_text = nullptr;
335  FormatMessageW (FORMAT_MESSAGE_FROM_SYSTEM |
336  FORMAT_MESSAGE_ALLOCATE_BUFFER |
337  FORMAT_MESSAGE_IGNORE_INSERTS,
338  nullptr, last_error,
339  MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
340  reinterpret_cast <wchar_t *> (&error_text), 0, nullptr);
341 
342  std::ostringstream err_str;
343  err_str << "opening the library '" << m_file << "' failed (error "
344  << last_error << "): ";
345  if (error_text != nullptr)
346  {
347  err_str << sys::u8_from_wstring (error_text);
348  LocalFree (error_text);
349  }
350  else
351  err_str << "Unknown error.";
352 
353  (*current_liboctave_error_handler) ("%s", err_str.str ().c_str ());
354  }
355 }
356 
357 octave_w32_shlib::~octave_w32_shlib (void)
358 {
359  if (m_handle)
360  FreeLibrary (m_handle);
361 }
362 
363 void *
364 octave_w32_shlib::global_search (const std::string& sym_name)
365 {
366  void *function = nullptr;
367 
368  HANDLE proc = GetCurrentProcess ();
369 
370  if (! proc)
371  (*current_liboctave_error_handler)
372  ("Unable to get handle to own process.");
373 
374  std::size_t lib_num = 64;
375  std::size_t size_lib = sizeof (HMODULE);
376  HMODULE *h_libs;
377  DWORD bytes_all_libs;
378  bool got_libs;
379 
380  // Get a list of all the libraries in own process.
381  h_libs = static_cast<HMODULE *> (malloc (size_lib*lib_num));
382  got_libs = EnumProcessModules (proc, h_libs, size_lib*lib_num,
383  &bytes_all_libs);
384  int ii = 0;
385  while (((size_lib*lib_num) < bytes_all_libs) && ii++ < 3)
386  {
387  lib_num = bytes_all_libs / size_lib;
388  h_libs = static_cast<HMODULE *> (realloc (h_libs, bytes_all_libs));
389  got_libs = EnumProcessModules (proc, h_libs, bytes_all_libs,
390  &bytes_all_libs);
391  }
392 
393  if (got_libs)
394  {
395  for (std::size_t i = 0; i < (bytes_all_libs / size_lib); i++)
396  {
397  // Check for function in library.
398  function = reinterpret_cast<void *>
399  (GetProcAddress (h_libs[i], sym_name.c_str ()));
400 
401  if (function)
402  break;
403  }
404  }
405 
406  // Release the handle to the process.
407  CloseHandle (proc);
408 
409  return function;
410 }
411 
412 void *
413 octave_w32_shlib::search (const std::string& name,
414  const dynamic_library::name_mangler& mangler)
415 {
416  void *function = nullptr;
417 
418  if (! m_search_all_loaded && ! is_open ())
419  (*current_liboctave_error_handler)
420  ("shared library %s is not open", m_file.c_str ());
421 
422  std::string sym_name = name;
423 
424  if (mangler)
425  sym_name = mangler (name);
426 
427  if (m_search_all_loaded)
428  function = global_search (sym_name);
429  else
430  function = reinterpret_cast<void *> (GetProcAddress (m_handle,
431  sym_name.c_str ()));
432 
433  return function;
434 }
435 
436 #endif
437 
440 {
441 #if defined (HAVE_DLOPEN_API)
442  return new octave_dlopen_shlib (f);
443 #elif defined (HAVE_LOADLIBRARY_API)
444  return new octave_w32_shlib (f);
445 #else
446  (*current_liboctave_error_handler)
447  ("support for dynamically loaded libraries was unavailable or disabled when liboctave was built");
448 #endif
449 }
450 
OCTAVE_END_NAMESPACE(octave)
OCTAVE_API bool remove_fcn_name(const std::string &)
Definition: oct-shlib.cc:152
static OCTAVE_API std::map< std::string, dynlib_rep * > s_instances
Definition: oct-shlib.h:107
OCTAVE_API void add_fcn_name(const std::string &)
Definition: oct-shlib.cc:141
static OCTAVE_API dynlib_rep * new_instance(const std::string &f)
Definition: oct-shlib.cc:439
static OCTAVE_API dynlib_rep * get_instance(const std::string &f, bool fake)
Definition: oct-shlib.cc:112
OCTAVE_API std::list< std::string > function_names(void) const
Definition: oct-shlib.cc:130
OCTAVE_API void fake_reload(void)
Definition: oct-shlib.cc:97
refcount< octave_idx_type > m_count
Definition: oct-shlib.h:101
OCTAVE_API bool is_out_of_date(void) const
Definition: oct-shlib.cc:90
void * search(const std::string &nm, const name_mangler &mangler=name_mangler()) const
Definition: oct-shlib.h:177
std::function< std::string(const std::string &)> name_mangler
Definition: oct-shlib.h:46
dynamic_library & operator=(const dynamic_library &sl)
Definition: oct-shlib.h:144
static OCTAVE_API dynlib_rep s_nil_rep
Definition: oct-shlib.h:121
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn
std::string dirname(const std::string &path)
Definition: file-ops.cc:360
static std::list< std::string > search(const std::string &path, const std::string &original_name, bool all)
Definition: kpse.cc:495
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:537
std::wstring u8_to_wstring(const std::string &utf8_string)
Definition: lo-sysdep.cc:514
std::list< dynamic_library > possibly_unreferenced_dynamic_libraries
Definition: oct-shlib.cc:63
int release_unreferenced_dynamic_libraries(void)
Definition: oct-shlib.cc:70
void * malloc(unsigned)