1 /**
2  * Orthanc - A Lightweight, RESTful DICOM Store
3  * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
4  * Department, University Hospital of Liege, Belgium
5  * Copyright (C) 2017-2021 Osimis S.A., Belgium
6  *
7  * This program is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public License
9  * as published by the Free Software Foundation, either version 3 of
10  * the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this program. If not, see
19  * <http://www.gnu.org/licenses/>.
20  **/
21 
22 
23 #include "PrecompiledHeaders.h"
24 #include "SharedLibrary.h"
25 
26 #include "Logging.h"
27 #include "OrthancException.h"
28 
29 #include <boost/filesystem.hpp>
30 
31 #if defined(_WIN32)
32 #include <windows.h>
33 #elif defined(__linux__) || (defined(__APPLE__) && defined(__MACH__)) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
34 #include <dlfcn.h>
35 #else
36 #error Support your platform here
37 #endif
38 
39 namespace Orthanc
40 {
SharedLibrary(const std::string & path)41   SharedLibrary::SharedLibrary(const std::string& path) :
42     path_(path),
43     handle_(NULL)
44   {
45 #if defined(_WIN32)
46     handle_ = ::LoadLibraryA(path_.c_str());
47     if (handle_ == NULL)
48     {
49       LOG(ERROR) << "LoadLibrary(" << path_ << ") failed: Error " << ::GetLastError();
50 
51       if (::GetLastError() == ERROR_BAD_EXE_FORMAT &&
52           sizeof(void*) == 4)
53       {
54         throw OrthancException(ErrorCode_SharedLibrary,
55                                "You are most probably trying to load a 64bit plugin into a 32bit version of Orthanc");
56       }
57       else if (::GetLastError() == ERROR_BAD_EXE_FORMAT &&
58                sizeof(void*) == 8)
59       {
60         throw OrthancException(ErrorCode_SharedLibrary,
61                                "You are most probably trying to load a 32bit plugin into a 64bit version of Orthanc");
62       }
63       else
64       {
65         throw OrthancException(ErrorCode_SharedLibrary);
66       }
67     }
68 
69 #elif defined(__linux__) || (defined(__APPLE__) && defined(__MACH__)) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
70 
71     /**
72      * "RTLD_LOCAL" is the default, and is only present to be
73      * explicit. "RTLD_DEEPBIND" was added in Orthanc 1.6.0, in order
74      * to avoid crashes while loading plugins from the LSB binaries of
75      * the Orthanc core.
76      *
77      * BUT this had no effect, and this results in a crash if loading
78      * the Python 2.7 plugin => We disabled it again in Orthanc 1.6.1.
79      **/
80 
81 #if 0 // && defined(RTLD_DEEPBIND)  // This is a GNU extension
82     // Disabled in Orthanc 1.6.1
83     handle_ = ::dlopen(path_.c_str(), RTLD_NOW | RTLD_LOCAL | RTLD_DEEPBIND);
84 #else
85     handle_ = ::dlopen(path_.c_str(), RTLD_NOW | RTLD_LOCAL);
86 #endif
87 
88     if (handle_ == NULL)
89     {
90       std::string explanation;
91       const char *tmp = ::dlerror();
92       if (tmp)
93       {
94         explanation = ": Error " + std::string(tmp);
95       }
96 
97       LOG(ERROR) << "dlopen(" << path_ << ") failed" << explanation;
98       throw OrthancException(ErrorCode_SharedLibrary);
99     }
100 
101 #else
102 #error Support your platform here
103 #endif
104   }
105 
~SharedLibrary()106   SharedLibrary::~SharedLibrary()
107   {
108     if (handle_)
109     {
110 #if defined(_WIN32)
111       ::FreeLibrary((HMODULE)handle_);
112 #elif defined(__linux__) || (defined(__APPLE__) && defined(__MACH__)) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
113       ::dlclose(handle_);
114 #else
115 #error Support your platform here
116 #endif
117     }
118   }
119 
120 
GetPath() const121   const std::string &SharedLibrary::GetPath() const
122   {
123     return path_;
124   }
125 
126 
GetFunctionInternal(const std::string & name)127   SharedLibrary::FunctionPointer SharedLibrary::GetFunctionInternal(const std::string& name)
128   {
129     if (!handle_)
130     {
131       throw OrthancException(ErrorCode_InternalError);
132     }
133 
134 #if defined(_WIN32)
135     return ::GetProcAddress((HMODULE)handle_, name.c_str());
136 #elif defined(__linux__) || (defined(__APPLE__) && defined(__MACH__)) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
137     return ::dlsym(handle_, name.c_str());
138 #else
139 #error Support your platform here
140 #endif
141   }
142 
143 
GetFunction(const std::string & name)144   SharedLibrary::FunctionPointer SharedLibrary::GetFunction(const std::string& name)
145   {
146     SharedLibrary::FunctionPointer result = GetFunctionInternal(name);
147 
148     if (result == NULL)
149     {
150       throw OrthancException(
151         ErrorCode_SharedLibrary,
152         "Shared library does not expose function \"" + name + "\"");
153     }
154     else
155     {
156       return result;
157     }
158   }
159 
160 
HasFunction(const std::string & name)161   bool SharedLibrary::HasFunction(const std::string& name)
162   {
163     return GetFunctionInternal(name) != NULL;
164   }
165 }
166