GNU Octave 11.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
 
Loading...
Searching...
No Matches
oct-shlib.cc
Go to the documentation of this file.
1////////////////////////////////////////////////////////////////////////
2//
3// Copyright (C) 1999-2026 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
33extern "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 ();
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 "oct-error.h"
54#include "oct-shlib.h"
55#include "str-vec.h"
56
57#if defined (HAVE_LOADLIBRARY_API)
58# include "oct-sysdep.h"
59#endif
60
62
64
65void
66dynamic_library::delete_later ()
67{
69}
70
71int
78
80 : m_count (1), m_fcn_names (), m_file (f), m_time_loaded (),
81 m_search_all_loaded (false)
82{
83 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
91bool
93{
94 sys::file_stat fs (m_file);
95 return (fs && fs.is_newer (m_time_loaded));
96}
97
98void
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
114dynamic_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 = instances ().find (f);
118 if (p != 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
131std::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
142void
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
153bool
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
169std::map<std::string, dynamic_library::dynlib_rep *>&
171{
172 // FIXME: This map is intentionally heap-allocated and never deleted to avoid
173 // UBSAN issues. Also see bug #53156 and the similar pattern in
174 // ov-typeinfo.cc. This creates a small memory leak that is reclaimed by the
175 // OS at process exit.
176 //
177 // This "leaky singleton" pattern is necessary because:
178 // 1. s_nil_rep is a file-scope static whose destructor calls instances()
179 // 2. possibly_unreferenced_dynamic_libraries may hold dynamic_library
180 // objects whose destructors also call instances()
181 // 3. We cannot guarantee the map outlives all these other statics
182 //
183 // By never destroying the map, we ensure it is always valid during shutdown.
184
185 static auto *s_instances = new std::map<std::string, dynlib_rep *> ();
186 return *s_instances;
187}
188
189dynamic_library::dynlib_rep dynamic_library::s_nil_rep;
190
191#if defined (HAVE_DLOPEN_API)
192
193class octave_dlopen_shlib : public dynamic_library::dynlib_rep
194{
195public:
196
197 octave_dlopen_shlib (const std::string& f);
198
199 OCTAVE_DISABLE_CONSTRUCT_COPY_MOVE (octave_dlopen_shlib)
200
201 ~octave_dlopen_shlib ();
202
203 void * search (const std::string& name,
204 const dynamic_library::name_mangler& mangler
206
207 // FIXME: this is possibly redundant because failure to open a library will
208 // normally throw an exception, avoiding the construction of an invalid
209 // library. Leave it here for possible future use.
210
211 bool is_open () const
212 {
213 return (m_search_all_loaded || m_library != nullptr);
214 }
215
216private:
217
218 void *m_library;
219};
220
221octave_dlopen_shlib::octave_dlopen_shlib (const std::string& f)
222 : dynamic_library::dynlib_rep (f), m_library (nullptr)
223{
224 int flags = 0;
225
226 // Use RTLD_NOW to resolve all symbols before dlopen returns.
227 // By using this option, dlopen will detect errors and Octave
228 // won't exit if there are unresolved symbols in the file we are
229 // loading, and we may even get a useful diagnostic.
230# if defined (RTLD_NOW)
231 flags |= RTLD_NOW;
232# endif
233
234 // Use RTLD_GLOBAL to export symbols from loaded objects so they are
235 // available to other subsequently loaded libraries.
236# if defined (RTLD_GLOBAL)
237 flags |= RTLD_GLOBAL;
238# endif
239
240 if (m_file.empty ())
241 {
242 m_search_all_loaded = true;
243 return;
244 }
245
246 m_library = dlopen (m_file.c_str (), flags);
247
248 if (! m_library)
249 {
250 const char *msg = dlerror ();
251
252 if (msg)
253 (*current_liboctave_error_handler)
254 ("%s: failed to load\nIncompatible version or missing dependency?"
255 "\n%s", m_file.c_str (), msg);
256 else
258 ("%s: failed to load\nIncompatible version or missing dependency?",
259 m_file.c_str ());
260 }
261}
262
263octave_dlopen_shlib::~octave_dlopen_shlib ()
264{
265 if (m_library)
266 dlclose (m_library);
267}
268
269void *
270octave_dlopen_shlib::search (const std::string& name,
271 const dynamic_library::name_mangler& mangler)
272{
273 void *function = nullptr;
274
275 if (! is_open ())
276 (*current_liboctave_error_handler)
277 ("shared library %s is not open", m_file.c_str ());
278
279 std::string sym_name = name;
280
281 if (mangler)
282 sym_name = mangler (name);
283
284 if (m_search_all_loaded)
285 function = dlsym (RTLD_DEFAULT, sym_name.c_str ());
286 else
287 function = dlsym (m_library, sym_name.c_str ());
288
289 return function;
290}
291
292#elif defined (HAVE_LOADLIBRARY_API)
293
294class octave_w32_shlib: public dynamic_library::dynlib_rep
295{
296public:
297
298 octave_w32_shlib (const std::string& f);
299
300 OCTAVE_DISABLE_COPY_MOVE (octave_w32_shlib)
301
302 ~octave_w32_shlib ();
303
304 void * search (const std::string& name,
305 const dynamic_library::name_mangler& mangler
307
308 void * global_search (const std::string& sym_name);
309
310 bool is_open () const
311 {
312 return (m_search_all_loaded || m_handle != nullptr);
313 }
314
315private:
316
317 HINSTANCE m_handle;
318};
319
320octave_w32_shlib::octave_w32_shlib (const std::string& f)
321 : dynamic_library::dynlib_rep (f), m_handle (nullptr)
322{
323 if (f.empty())
324 {
325 m_search_all_loaded = true;
326 return;
327 }
328
329 std::string dir = sys::file_ops::dirname (f);
330 std::wstring wdir = sys::u8_to_wstring (dir);
331 SetDllDirectoryW (dir.empty ()
332 ? nullptr : wdir.c_str ());
333
334 std::wstring wfile = sys::u8_to_wstring (m_file);
335 m_handle = LoadLibraryW (wfile.c_str ());
336
337 SetDllDirectoryW (nullptr);
338
339 if (! m_handle)
340 {
341 DWORD last_error = GetLastError ();
342
343 wchar_t *error_text = nullptr;
344 FormatMessageW (FORMAT_MESSAGE_FROM_SYSTEM |
345 FORMAT_MESSAGE_ALLOCATE_BUFFER |
346 FORMAT_MESSAGE_IGNORE_INSERTS,
347 nullptr, last_error,
348 MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
349 reinterpret_cast<wchar_t *> (&error_text), 0, nullptr);
350
351 std::ostringstream err_str;
352 err_str << "opening the library '" << m_file << "' failed (error "
353 << last_error << "): ";
354 if (error_text != nullptr)
355 {
356 err_str << sys::u8_from_wstring (error_text);
357 LocalFree (error_text);
358 }
359 else
360 err_str << "Unknown error.";
361
362 (*current_liboctave_error_handler) ("%s", err_str.str ().c_str ());
363 }
364}
365
366octave_w32_shlib::~octave_w32_shlib ()
367{
368 if (m_handle)
369 FreeLibrary (m_handle);
370}
371
372void *
373octave_w32_shlib::global_search (const std::string& sym_name)
374{
375 void *function = nullptr;
376
377 HANDLE proc = GetCurrentProcess ();
378
379 if (! proc)
380 (*current_liboctave_error_handler)
381 ("Unable to get handle to own process.");
382
383 std::size_t lib_num = 64;
384 std::size_t size_lib = sizeof (HMODULE);
385 HMODULE *h_libs;
386 DWORD bytes_all_libs;
387 bool got_libs;
388
389 // Get a list of all the libraries in own process.
390 h_libs = static_cast<HMODULE *> (malloc (size_lib*lib_num));
391 got_libs = EnumProcessModules (proc, h_libs, size_lib*lib_num,
392 &bytes_all_libs);
393 int ii = 0;
394 while (((size_lib*lib_num) < bytes_all_libs) && ii++ < 3)
395 {
396 lib_num = bytes_all_libs / size_lib;
397 h_libs = static_cast<HMODULE *> (realloc (h_libs, bytes_all_libs));
398 got_libs = EnumProcessModules (proc, h_libs, bytes_all_libs,
399 &bytes_all_libs);
400 }
401
402 if (got_libs)
403 {
404 for (std::size_t i = 0; i < (bytes_all_libs / size_lib); i++)
405 {
406 // Check for function in library.
407 function = reinterpret_cast<void *>
408 (GetProcAddress (h_libs[i], sym_name.c_str ()));
409
410 if (function)
411 break;
412 }
413 }
414
415 // Release the handle to the process.
416 CloseHandle (proc);
417
418 return function;
419}
420
421void *
422octave_w32_shlib::search (const std::string& name,
423 const dynamic_library::name_mangler& mangler)
424{
425 void *function = nullptr;
426
427 if (! m_search_all_loaded && ! is_open ())
428 (*current_liboctave_error_handler)
429 ("shared library %s is not open", m_file.c_str ());
430
431 std::string sym_name = name;
432
433 if (mangler)
434 sym_name = mangler (name);
435
436 if (m_search_all_loaded)
437 function = global_search (sym_name);
438 else
439 function = reinterpret_cast<void *> (GetProcAddress (m_handle,
440 sym_name.c_str ()));
441
442 return function;
443}
444
445#endif
446
449{
450#if defined (HAVE_DLOPEN_API)
451 return new octave_dlopen_shlib (f);
452#elif defined (HAVE_LOADLIBRARY_API)
453 return new octave_w32_shlib (f);
454#else
455 (*current_liboctave_error_handler)
456 ("support for dynamically loaded libraries was unavailable or disabled when liboctave was built");
457#endif
458}
459
460OCTAVE_END_NAMESPACE(octave)
static std::map< std::string, dynlib_rep * > & instances()
Definition oct-shlib.cc:170
bool remove_fcn_name(const std::string &)
Definition oct-shlib.cc:154
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:448
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:102
std::function< std::string(const std::string &)> name_mangler
Definition oct-shlib.h:45
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn
OCTAVE_NORETURN liboctave_error_handler current_liboctave_error_handler
Definition lo-error.c:41
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)
F77_RET_T const F77_DBLE const F77_DBLE * f