NCBI C++ ToolKit
ncbidll.cpp
Go to the documentation of this file.

Go to the SVN repository for this file.

1 /* $Id: ncbidll.cpp 99164 2023-02-21 16:49:05Z ivanov $
2  * ===========================================================================
3  *
4  * PUBLIC DOMAIN NOTICE
5  * National Center for Biotechnology Information
6  *
7  * This software/database is a "United States Government Work" under the
8  * terms of the United States Copyright Act. It was written as part of
9  * the author's official duties as a United States Government employee and
10  * thus cannot be copyrighted. This software/database is freely available
11  * to the public for use. The National Library of Medicine and the U.S.
12  * Government have not placed any restriction on its use or reproduction.
13  *
14  * Although all reasonable efforts have been taken to ensure the accuracy
15  * and reliability of the software and data, the NLM and the U.S.
16  * Government do not and cannot warrant the performance or results that
17  * may be obtained by using this software or data. The NLM and the U.S.
18  * Government disclaim all warranties, express or implied, including
19  * warranties of performance, merchantability or fitness for any particular
20  * purpose.
21  *
22  * Please cite the author in any work or product based on this material.
23  *
24  * ===========================================================================
25  *
26  * Author: Vladimir Ivanov, Denis Vakatov
27  *
28  * File Description:
29  * Portable DLL handling
30  *
31  */
32 
33 #include <ncbi_pch.hpp>
34 #include <corelib/ncbidll.hpp>
35 #include <corelib/ncbifile.hpp>
36 #include <corelib/ncbiapp_api.hpp>
37 #include <corelib/error_codes.hpp>
38 #include <common/ncbi_sanitizers.h>
39 #include "ncbisys.hpp"
40 
41 
42 #if defined(NCBI_OS_MSWIN)
43 # include <corelib/ncbi_os_mswin.hpp>
44 #elif defined(NCBI_OS_UNIX)
45 # ifdef HAVE_DLFCN_H
46 # include <dlfcn.h>
47 # ifndef RTLD_LOCAL /* missing on Cygwin? */
48 # define RTLD_LOCAL 0
49 # endif
50 # endif
51 # ifdef NCBI_OS_DARWIN
52 # include <mach-o/dyld.h>
53 # endif
54 #else
55 # error "Class CDll defined only for MS Windows and UNIX platforms"
56 #endif
57 
58 #if defined(NCBI_OS_MSWIN)
59 # pragma warning (disable : 4191)
60 #endif
61 
62 
63 #define NCBI_USE_ERRCODE_X Corelib_Dll
64 
65 
67 
68 
69 // Platform-dependent DLL handle type definition
70 struct SDllHandle {
71 #if defined(NCBI_OS_MSWIN)
72  HMODULE handle;
73 #elif defined(NCBI_OS_UNIX)
74  void* handle;
75 #endif
76 };
77 
78 // Check flag bits
79 #define F_ISSET(mask) ((m_Flags & (mask)) == (mask))
80 // Clean up an all non-default bits in group if all bits are set
81 #define F_CLEAN_REDUNDANT(group) \
82  if (F_ISSET(group)) m_Flags &= ~unsigned((group) & ~unsigned(fDefault))
83 
84 
85 CDll::CDll(const string& name, TFlags flags)
86 {
87  x_Init(kEmptyStr, name, flags);
88 }
89 
90 CDll::CDll(const string& path, const string& name, TFlags flags)
91 {
92  x_Init(path, name, flags);
93 }
94 
95 CDll::CDll(const string& name, ELoad when_to_load, EAutoUnload auto_unload,
96  EBasename treate_as)
97 {
98  x_Init(kEmptyStr, name,
99  TFlags(when_to_load) | TFlags(auto_unload) | TFlags(treate_as));
100 }
101 
102 CDll::CDll(const string& path, const string& name, ELoad when_to_load,
103  EAutoUnload auto_unload, EBasename treate_as)
104 {
105  x_Init(path, name,
106  TFlags(when_to_load) | TFlags(auto_unload) | TFlags(treate_as));
107 }
108 
109 
111 {
112  // Unload DLL automatically
113  if ( F_ISSET(fAutoUnload) ) {
114  try {
115  Unload();
116  } catch(CException& e) {
117  NCBI_REPORT_EXCEPTION_X(1, "CDll destructor", e);
118  }
119  }
120  delete m_Handle;
121 }
122 
123 
124 void CDll::x_Init(const string& path, const string& name, TFlags flags)
125 {
126  // Save flags
127  m_Flags = flags;
128 
129  // Reset redundant flags
134 
135  // Init members
136  m_Handle = 0;
137  string x_name = name;
138 #if defined(NCBI_OS_MSWIN)
139  NStr::ToLower(x_name);
140 #endif
141  // Process DLL name
142  if (F_ISSET(fBaseName) &&
143  name.find_first_of(":/\\") == NPOS &&
144  !CDirEntry::MatchesMask(name.c_str(),
146  ) {
147  // "name" is basename
148  x_name = NCBI_PLUGIN_PREFIX + x_name + NCBI_PLUGIN_SUFFIX;
149  }
150  m_Name = CDirEntry::ConcatPath(path, x_name);
151  // Load DLL now if indicated
152  if (F_ISSET(fLoadNow)) {
153  Load();
154  }
155 }
156 
157 
158 void CDll::Load(void)
159 {
160  // DLL is already loaded
161  if ( m_Handle ) {
162  return;
163  }
164  // Load DLL
165  _TRACE("Loading dll: "<<m_Name);
166 #if defined(NCBI_OS_MSWIN)
167  UINT errMode = SetErrorMode(SEM_FAILCRITICALERRORS);
168  HMODULE handle = LoadLibrary(_T_XCSTRING(m_Name));
169  SetErrorMode(errMode);
170 #elif defined(NCBI_OS_UNIX)
171 # ifdef HAVE_DLFCN_H
172  int flags = RTLD_LAZY | (F_ISSET(fLocal) ? RTLD_LOCAL : RTLD_GLOBAL);
173  void* handle = dlopen(m_Name.c_str(), flags);
174 # else
175  void* handle = 0;
176 # endif
177 #endif
178  if ( !handle ) {
179  x_ThrowException("CDll::Load");
180  }
181  m_Handle = new SDllHandle;
182  m_Handle->handle = handle;
183 }
184 
185 
186 void CDll::Unload(void)
187 {
188  // DLL is not loaded
189  if ( !m_Handle ) {
190  return;
191  }
192  _TRACE("Unloading dll: "<<m_Name);
193  // Unload DLL
194 #if defined(NCBI_OS_MSWIN)
195  BOOL unloaded = FreeLibrary(m_Handle->handle);
196 #elif defined(NCBI_OS_UNIX)
197 # ifdef HAVE_DLFCN_H
198  bool unloaded = dlclose(m_Handle->handle) == 0;
199 # else
200  bool unloaded = false;
201 # endif
202 #endif
203  if ( !unloaded ) {
204  x_ThrowException("CDll::Unload");
205  }
206 
207  delete m_Handle;
208  m_Handle = 0;
209 }
210 
211 
213 {
214  // If DLL is not yet loaded
215  if ( !m_Handle ) {
216  Load();
217  }
218  _TRACE("Getting entry point: "<<name);
219  TEntryPoint entry;
220 
221  // Return address of entry (function or data)
222 #if defined(NCBI_OS_MSWIN)
223  FARPROC ptr = GetProcAddress(m_Handle->handle, name.c_str());
224 #elif defined(NCBI_OS_UNIX) && defined(HAVE_DLFCN_H)
225  void* ptr = 0;
226  ptr = dlsym(m_Handle->handle, name.c_str());
227 #elif defined(NCBI_OS_DARWIN)
228  NSModule module = (NSModule)m_Handle->handle;
229  NSSymbol nssymbol = NSLookupSymbolInModule(module, name.c_str());
230  void* ptr = 0;
231  ptr = NSAddressOfSymbol(nssymbol);
232  if (ptr == NULL) {
233  ptr = dlsym (m_Handle->handle, name.c_str());
234  }
235 #else
236  void* ptr = 0;
237 #endif
238  entry.func = (FEntryPoint)ptr;
239  entry.data = ptr;
240  return entry;
241 }
242 
243 
244 void CDll::x_ThrowException(const string& what)
245 {
246 #if defined(NCBI_OS_MSWIN)
247  TXChar* ptr = NULL;
248  FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
249  FORMAT_MESSAGE_FROM_SYSTEM |
250  FORMAT_MESSAGE_IGNORE_INSERTS,
251  NULL, GetLastError(),
252  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
253  (TXChar*) &ptr, 0, NULL);
254  string errmsg = ptr ? _T_CSTRING(ptr) : "unknown reason";
255  LocalFree(ptr);
256 #elif defined(NCBI_OS_UNIX)
257 # ifdef HAVE_DLFCN_H
258  const char* errmsg = dlerror();
259  if ( !errmsg ) {
260  errmsg = "unknown reason";
261  }
262 # else
263  const char* errmsg = "No DLL support on this platform.";
264 # endif
265 #endif
266 
267  NCBI_THROW(CCoreException, eDll, what + " [" + m_Name +"]: " + errmsg);
268 }
269 
270 
271 CDllResolver::CDllResolver(const string& entry_point_name,
272  CDll::EAutoUnload unload)
273  : m_AutoUnloadDll(unload)
274 {
275  m_EntryPoinNames.push_back(entry_point_name);
276 }
277 
278 CDllResolver::CDllResolver(const vector<string>& entry_point_names,
279  CDll::EAutoUnload unload)
280  : m_AutoUnloadDll(unload)
281 {
282  m_EntryPoinNames = entry_point_names;
283 }
284 
286 {
287  Unload();
288 }
289 
291  const string& driver_name)
292 {
293  CDll* dll = nullptr;
294  try {
295  {{
296  // Deliberate leak to avoid automatic unloading DLL from a memory
299  }}
301 
302  SResolvedEntry entry_point(dll);
303 
304  ITERATE(vector<string>, it, m_EntryPoinNames) {
305  string entry_point_name;
306 
307  const string& dll_name = dll->GetName();
308 
309  if ( !dll_name.empty() ) {
310  string base_name;
311  CDirEntry::SplitPath(dll_name, 0, &base_name, 0);
312  NStr::Replace(*it, "${basename}", base_name, entry_point_name);
313  if (!driver_name.empty()) {
314  NStr::Replace(*it, "${driver}", driver_name, entry_point_name);
315  }
316  }
317 
318  // Check for the BASE library name macro
319 
320  if ( entry_point_name.empty() )
321  continue;
322  p = dll->GetEntryPoint(entry_point_name);
323  if ( p.data ) {
324  entry_point.entry_points.push_back(SNamedEntryPoint(entry_point_name, p));
325  }
326  } // ITERATE
327 
328  if ( entry_point.entry_points.empty() ) {
329  dll->Unload();
330  delete dll;
331  return false;
332  }
333  m_ResolvedEntries.push_back(entry_point);
334  }
335  catch (CCoreException& ex)
336  {
337  if (dll != nullptr) {
338  dll->Unload();
339  delete dll;
340  }
341  if (ex.GetErrCode() != CCoreException::eDll) {
342  throw;
343  }
344  NCBI_REPORT_EXCEPTION_X(2, "CDllResolver::TryCandidate() failed", ex);
345  return false;
346  }
347 
348  return true;
349 }
350 
351 static inline
352 string s_GetProgramPath(void)
353 {
354  string dir;
357  return dir;
358 }
359 
360 void CDllResolver::x_AddExtraDllPath(vector<string>& paths, TExtraDllPath which)
361 {
362  if (which == fNoExtraDllPath) {
363  // Nothing to do
364  return;
365  }
366 
367  // Add program executable path
368 
369  if ((which & fProgramPath) != 0) {
370  string dir = s_GetProgramPath();
371  if ( !dir.empty() ) {
372  paths.push_back(dir);
373  }
374  }
375 
376  // Add systems directories
377 
378  if ((which & fSystemDllPath) != 0) {
379 #if defined(NCBI_OS_MSWIN)
380  // Get Windows system directories
381  TXChar buf[MAX_PATH+1];
382  UINT len = GetSystemDirectory(buf, MAX_PATH+1);
383  if (len>0 && len<=MAX_PATH) {
384  paths.push_back(_T_STDSTRING(buf));
385  }
386  len = GetWindowsDirectory(buf, MAX_PATH+1);
387  if (len>0 && len<=MAX_PATH) {
388  paths.push_back(_T_STDSTRING(buf));
389  }
390  // Parse PATH environment variable
391  const TXChar* env = NcbiSys_getenv(_TX("PATH"));
392  if (env && *env) {
393  NStr::Split(_T_STDSTRING(env), ";", paths);
394  }
395 
396 #elif defined(NCBI_OS_UNIX)
397  // From LD_LIBRARY_PATH environment variable
398  const char* env = getenv("LD_LIBRARY_PATH");
399  if (env && *env) {
400  NStr::Split(env, ":", paths);
401  }
402 #endif
403  }
404 
405  // Add hard coded runpath
406 
407  if ((which & fToolkitDllPath) != 0) {
408  const char* runpath = NCBI_GetRunpath();
409  if (runpath && *runpath) {
410 # if defined(NCBI_OS_MSWIN)
411  NStr::Split(runpath, ";", paths);
412 # elif defined(NCBI_OS_UNIX)
413  vector<string> tokenized;
414  NStr::Split(runpath, ":", tokenized);
415  ITERATE(vector<string>, i, tokenized) {
416  if (i->find("$ORIGIN") == NPOS) {
417  paths.push_back(*i);
418  } else {
419  string dir = s_GetProgramPath();
420  if ( !dir.empty() ) {
421  // Need to know the $ORIGIN else discard path.
422  paths.push_back(NStr::Replace(*i, "$ORIGIN", dir));
423  }
424  }
425  }
426 # else
427  paths.push_back(runpath);
428 # endif
429  }
430  }
431 
432  return;
433 }
434 
436 {
439  it->dll->Unload();
440  }
441  delete it->dll;
442  }
443  m_ResolvedEntries.resize(0);
444 }
445 
446 
CCoreException –.
Definition: ncbiexpt.hpp:1476
CDll –.
Definition: ncbidll.hpp:107
static uch flags
const char * file_name[]
static HENV env
Definition: transaction2.c:38
int BOOL
Definition: sybdb.h:150
static string GetAppName(EAppNameType name_type=eBaseName, int argc=0, const char *const *argv=NULL)
Definition: ncbiapp.cpp:1390
#define ITERATE(Type, Var, Cont)
ITERATE macro to sequence through container elements.
Definition: ncbimisc.hpp:815
#define NON_CONST_ITERATE(Type, Var, Cont)
Non constant version of ITERATE macro.
Definition: ncbimisc.hpp:822
@ eFullName
per GetProgramExecutablePath(eIgnoreLinks)
#define NULL
Definition: ncbistd.hpp:225
#define _TRACE(message)
Definition: ncbidbg.hpp:122
const string & GetName() const
Get the name of the DLL file.
Definition: ncbidll.hpp:325
SDllHandle * m_Handle
DLL handle.
Definition: ncbidll.hpp:358
FEntryPoint func
Do not call this func without type cast!
Definition: ncbidll.hpp:305
TFlags m_Flags
Flags.
Definition: ncbidll.hpp:359
void(* FEntryPoint)(char ****Do_Not_Call_This)
Fake, uncallable function pointer.
Definition: ncbidll.hpp:301
EBasename
Whether to transform the DLL basename.
Definition: ncbidll.hpp:152
void Load(void)
Load DLL.
Definition: ncbidll.cpp:158
void x_Init(const string &path, const string &name, TFlags flags)
Helper method to initialize object.
Definition: ncbidll.cpp:124
bool TryCandidate(const string &file_name, const string &driver_name=kEmptyStr)
Try to load DLL from the specified file and resolve the entry point.
Definition: ncbidll.cpp:290
int TExtraDllPath
Definition: ncbidll.hpp:473
vector< SResolvedEntry > TEntries
Container, keeps list of all resolved entry points.
Definition: ncbidll.hpp:402
vector< SNamedEntryPoint > entry_points
list of DLL entry points
Definition: ncbidll.hpp:394
CDllResolver(const string &entry_point_name, CDll::EAutoUnload unload=CDll::eAutoUnload)
Constructor.
Definition: ncbidll.cpp:271
EAutoUnload
Whether to unload DLL in the destructor.
Definition: ncbidll.hpp:143
string m_Name
DLL name.
Definition: ncbidll.hpp:357
~CDll(void)
Destructor.
Definition: ncbidll.cpp:110
CDll::EAutoUnload m_AutoUnloadDll
Definition: ncbidll.hpp:561
ELoad
When to load DLL.
Definition: ncbidll.hpp:137
void Unload()
Unload all resolved DLLs.
Definition: ncbidll.cpp:435
unsigned int TFlags
Binary OR of "EFlags".
Definition: ncbidll.hpp:130
TEntryPoint GetEntryPoint(const string &name)
Helper find method for getting a DLLs entry point.
Definition: ncbidll.cpp:212
#define NCBI_PLUGIN_MIN_SUFFIX
Definition: ncbidll.hpp:58
#define NCBI_PLUGIN_SUFFIX
Definition: ncbidll.hpp:67
void Unload(void)
Unload DLL.
Definition: ncbidll.cpp:186
~CDllResolver()
Destructor.
Definition: ncbidll.cpp:285
#define NCBI_PLUGIN_PREFIX
Definition: ncbidll.hpp:57
vector< string > m_EntryPoinNames
Candidate entry points.
Definition: ncbidll.hpp:559
CDll(const string &name, TFlags flags)
Constructor.
Definition: ncbidll.cpp:85
void x_ThrowException(const string &what)
Helper method to throw exception with system-specific error message.
Definition: ncbidll.cpp:244
TEntries m_ResolvedEntries
Definition: ncbidll.hpp:560
void x_AddExtraDllPath(vector< string > &paths, TExtraDllPath which)
Get the DLL search paths related to the given standard path group.
Definition: ncbidll.cpp:360
@ fLoadNow
When to load DLL.
Definition: ncbidll.hpp:115
@ fAutoUnload
Whether to unload DLL in the destructor.
Definition: ncbidll.hpp:118
@ fBaseName
Whether to transform the DLL basename.
Definition: ncbidll.hpp:121
@ fLocal
Load as RTLD_LOCAL.
Definition: ncbidll.hpp:126
@ fLoadLater
Load DLL later, using method Load()
Definition: ncbidll.hpp:116
@ fExactName
Use the name "as is".
Definition: ncbidll.hpp:122
@ fGlobal
Specify how to load symbols from DLL.
Definition: ncbidll.hpp:125
@ fNoAutoUnload
Unload DLL later, using method Unload()
Definition: ncbidll.hpp:119
@ eAutoUnload
Definition: ncbidll.hpp:144
#define NCBI_THROW(exception_class, err_code, message)
Generic macro to throw an exception, given the exception class, error code and message string.
Definition: ncbiexpt.hpp:704
TErrCode GetErrCode(void) const
Definition: ncbiexpt.hpp:1493
#define NCBI_REPORT_EXCEPTION_X(err_subcode, title, ex)
Generate a report on the exception with default error code and given subcode.
Definition: ncbiexpt.hpp:761
@ eDll
Dll error.
Definition: ncbiexpt.hpp:1484
static string ConcatPath(const string &first, const string &second)
Concatenate two parts of the path for the current OS.
Definition: ncbifile.cpp:776
static bool MatchesMask(const string &name, const string &mask, NStr::ECase use_case=NStr::eCase)
Match a "name" against a simple filename "mask".
Definition: ncbifile.hpp:3968
static void SplitPath(const string &path, string *dir=0, string *base=0, string *ext=0)
Split a path string into its basic components.
Definition: ncbifile.cpp:358
#define END_NCBI_SCOPE
End previously defined NCBI scope.
Definition: ncbistl.hpp:103
#define BEGIN_NCBI_SCOPE
Define ncbi namespace.
Definition: ncbistl.hpp:100
#define kEmptyStr
Definition: ncbistr.hpp:123
static list< string > & Split(const CTempString str, const CTempString delim, list< string > &arr, TSplitFlags flags=0, vector< SIZE_TYPE > *token_pos=NULL)
Split a string using specified delimiters.
Definition: ncbistr.cpp:3452
char TXChar
Definition: ncbistr.hpp:172
#define NPOS
Definition: ncbistr.hpp:133
#define _T_STDSTRING(x)
Definition: ncbistr.hpp:180
#define _T_CSTRING(x)
Definition: ncbistr.hpp:182
static string & Replace(const string &src, const string &search, const string &replace, string &dst, SIZE_TYPE start_pos=0, SIZE_TYPE max_replace=0, SIZE_TYPE *num_replace=0)
Replace occurrences of a substring within a string.
Definition: ncbistr.cpp:3305
#define _T_XCSTRING(x)
Definition: ncbistr.hpp:181
#define _TX(x)
Definition: ncbistr.hpp:176
static string & ToLower(string &str)
Convert string to lower case – string& version.
Definition: ncbistr.cpp:405
Definition of all error codes used in corelib (xncbi.lib).
char * buf
int i
int len
Defines MS Windows specifics for our "C++" code.
Common macro to detect used sanitizers and suppress memory leaks if run under LeakSanitizer.
#define NCBI_LSAN_DISABLE_GUARD
const char * NCBI_GetRunpath(void)
Get run path.
#define F_CLEAN_REDUNDANT(group)
Definition: ncbidll.cpp:81
#define F_ISSET(mask)
Definition: ncbidll.cpp:79
static string s_GetProgramPath(void)
Definition: ncbidll.cpp:352
Define class Dll and for Portable DLL handling.
Defines classes: CDirEntry, CFile, CDir, CSymLink, CMemoryFile, CFileUtil, CFileLock,...
unsigned int UINT
Definition: sqltypes.h:263
#define NcbiSys_getenv
Definition: ncbisys.hpp:90
DLL entry point name -> function pair.
Definition: ncbidll.hpp:378
DLL resolution descriptor.
Definition: ncbidll.hpp:392
HMODULE handle
Definition: ncbidll.cpp:72
Entry point – pointer to either a function or a data.
Definition: ncbidll.hpp:304
Modified on Fri Sep 20 14:58:18 2024 by modify_doxy.py rev. 669887