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