charon-core  0.3.1
UnixPluginLoader.cpp
Go to the documentation of this file.
1 /* This file is part of Charon.
2 
3  Charon is free software: you can redistribute it and/or modify
4  it under the terms of the GNU Lesser General Public License as published by
5  the Free Software Foundation, either version 3 of the License, or
6  (at your option) any later version.
7 
8  Charon is distributed in the hope that it will be useful,
9  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  GNU Lesser General Public License for more details.
12 
13  You should have received a copy of the GNU Lesser General Public License
14  along with Charon. If not, see <http://www.gnu.org/licenses/>.
15  */
22 #ifdef APPLE
24 #define LIBRARY_EXTENSION ".dylib"
25 #else
26 #define LIBRARY_EXTENSION ".so"
27 #endif /*APPLE*/
28 
29 #include <dlfcn.h>
30 #include "../include/charon-core/UnixPluginLoader.h"
31 
32 #ifdef USE_LIBELF
33 #include "../include/charon-core/configVersion.h"
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <fcntl.h>
37 #include <unistd.h>
38 #include <libelf.h>
39 #include <gelf.h>
40 #endif
41 
43  const std::string & n,
44  std::vector<std::string>& plpaths,
45  std::string &lSuffix,
47  AbstractPluginLoader(n,plpaths,lSuffix,versionCheck),
48  libHandle(NULL)
49 {
50 }
51 
53  std::string path, pathS;
54 
55  // find plugin file
56  for(std::vector<std::string>::const_iterator cur = pluginPaths.begin();
57  cur != pluginPaths.end(); cur++) {
58  path = *cur + "/lib" + pluginName + LIBRARY_EXTENSION;
59  if(libSuffix.size() > 0) {
60  pathS = *cur + "/lib" + pluginName + libSuffix + LIBRARY_EXTENSION;
61  if (FileTool::exists(pathS)) {
62  // prefer suffixed over plain version
63  path = pathS;
64  break;
65  }
66  }
67  if (FileTool::exists(path)) {
68  break;
69  }
70  }
71  if (!FileTool::exists(path)) {
72  throw PluginException("Failed to find the plugin \"" + pluginName
73  + "\". The file" + pluginName + LIBRARY_EXTENSION +
74  + " could not be found."
75  + (libSuffix.size() > 0 ?
76  " (Possible suffix: " + libSuffix + ")" :
77  " (suffix disabled)"),
79  }
80 
81  sout << "(DD) File: " << path << std::endl;
82 
83 #ifdef USE_LIBELF
84  // try to determine charon-core version from plugin before dlopening
85  // to avoid version mismatch crashes, this can be disabled in the
86  // plugin manager configuration (PluginManager::_versionCheck)
87 #if defined(__x86_64__)
88  typedef Elf64_Ehdr Elf_Ehdr;
89  const unsigned char elfClass = ELFCLASS64;
90  const std::string elfClassDesc = "64 bit";
91 #else
92  typedef Elf32_Ehdr Elf_Ehdr;
93  const unsigned char elfClass = ELFCLASS32;
94  const std::string elfClassDesc = "32 bit";
95 #endif
96  if (_versionCheck != PluginManagerInterface::PluginVersionIgnore) {
97  int fd = open(path.c_str(),O_RDONLY);
98  if (fd < 0) {
99  throw PluginException("Failed to open the plugin \"" + pluginName
100  + "\". Maybe you do not have read permissions.",
102  }
103  struct stat fStats;
104  if (fstat(fd, &fStats)) {
105  close(fd);
106  throw PluginException("Failed to fstat the plugin \"" + pluginName
108  }
109  char* buf = new char[fStats.st_size];
110  if (read(fd, buf, fStats.st_size) < fStats.st_size) {
111  delete[] buf;
112  close(fd);
113  throw PluginException("Failed to read the plugin \"" + pluginName
115  }
116  if (elf_version(EV_CURRENT) == EV_NONE) {
117  sout << "(WW) Elf Library is out of date!" << std::endl;
118  }
119  Elf_Ehdr* eHeader = (Elf_Ehdr*) buf;
120  // check ELF magic bytes
121  if (
122  eHeader->e_ident[EI_MAG0]!=ELFMAG0 ||
123  eHeader->e_ident[EI_MAG1]!=ELFMAG1 ||
124  eHeader->e_ident[EI_MAG2]!=ELFMAG2 ||
125  eHeader->e_ident[EI_MAG3]!=ELFMAG3) {
126  delete[] buf;
127  close(fd);
128  throw PluginException("ELF check failed (magic bytes): File "
129  + path + " is no ELF file.",
131  }
132  // check for possible 32/64 bit mismatch
133  if (eHeader->e_ident[EI_CLASS] != elfClass) {
134  delete[] buf;
135  close(fd);
136  throw PluginException("ELF check failed (architecture): File "
137  + path + " is no " + elfClassDesc + " ELF file.",
139  }
140  // ELF version check
141  if (eHeader->e_ident[EI_VERSION] != EV_CURRENT) {
142  delete[] buf;
143  close(fd);
144  throw PluginException("ELF check failed (version): File "
145  + path + " was compiled with another ELF version.",
147  }
148  Elf* elf = elf_begin(fd,ELF_C_READ,NULL);
149  Elf_Scn* scn = 0;
150  // iterate through elf sections and look for .charon-plugin
151  while ((scn=elf_nextscn(elf,scn))!=0) {
152  GElf_Shdr shdr;
153  gelf_getshdr(scn,&shdr);
154  std::string sName = elf_strptr(elf,eHeader->e_shstrndx,shdr.sh_name);
155  if (sName == ".charon-plugin") {
156  break;
157  }
158  }
159 
160  bool checkPassed = false;
161 
162  std::string checkFailMsg;
163  PluginException::ErrorCode checkErrCode =
165  std::ostringstream foundVersion;
166 
167  if (!scn) {
168  checkFailMsg = "No Plugin Section found in ELF file";
170  }
171  else {
172  Elf_Data* eData = 0;
173  eData = elf_getdata(scn, eData);
174  // check data type and size
175  if (eData->d_type != ELF_T_BYTE) {
176  checkFailMsg = "Wrong Plugin Section Data Type";
177  }
178  else if (eData->d_size < 3) {
179  checkFailMsg = "Wrong Plugin Section Data Size";
180  }
181  else {
182  const unsigned char* content =
183  (const unsigned char*) eData->d_buf;
184  foundVersion << (int) content[0] << "."
185  << (int) content[1] << "." << (int) content[2];
186  // check version
187  if (content[0] != CHARON_CORE_VERSION_MAJOR) {
188  checkFailMsg = "plugin major version mismatch";
189  }
190  else if (content[1] != CHARON_CORE_VERSION_MINOR) {
191  checkFailMsg = "plugin minor version mismatch";
192  }
193  else if (content[2] != CHARON_CORE_VERSION_PATCH) {
194  checkFailMsg = "plugin patch version mismatch";
195  }
196  else {
197  checkPassed = true;
198  sout << "(DD) plugin ELF check successful" << std::endl;
199  }
200  }
201  }
202  delete[] buf;
203  close(fd);
204 
205  if (!checkPassed) {
206  switch (_versionCheck) {
207  case PluginManagerInterface::PluginVersionWarn:
208  if (checkErrCode == PluginException::VERSION_MISSMATCH) {
209  checkFailMsg = checkFailMsg
210  + "\n(WW) \tcharon-core version: "
212  + "\n(WW) \tplugin compiled with version: "
213  + foundVersion.str();
214  }
215  sout << "(WW) ELF Check of plugin \"" << pluginName
216  << "\" failed: " << checkFailMsg << std::endl;
217  break;
218  case PluginManagerInterface::PluginVersionDiscard:
219  if (checkErrCode == PluginException::VERSION_MISSMATCH) {
220  checkFailMsg = checkFailMsg
221  + "\n\tcharon-core version: "
223  + "\n\tplugin compiled with version: "
224  + foundVersion.str();
225  }
226  throw PluginException("ELF check failed: " + checkFailMsg,
227  pluginName, checkErrCode);
228  break;
229  default:
230  break;
231  };
232  }
233  }
234 #endif
235 
236  // try to dlopen the shared library
237  libHandle = dlopen(path.c_str(), RTLD_LAZY | RTLD_GLOBAL);
238  if (!libHandle) {
239  throw PluginException("Failed to load the plugin \"" + pluginName
240  + "\". Maybe the file is damaged."
241  + "\n Description of the error:\n" + dlerror(),
243  }
244 
245  // get function pointers from shared library
246  *reinterpret_cast<void**>(&create) = dlsym(libHandle, "create");
247  *reinterpret_cast<void**>(&destroy) = dlsym(libHandle, "destroy");
248  // This way, there is no cast between a
249  // pointer-to-function and pointer-to-object
250  // (-> not allowed by ISO C++!)
251  //
252  // The *adress* of the target function (which is an object)
253  // is casted to void**, then dereferenced immediatly, so the
254  // left hand side is an object pointer with same content as
255  // the function pointers create/destroy.
256 
257  if (!create) {
258  dlclose(libHandle);
259  libHandle = NULL;
260  throw PluginException(
261  "function \"create\" missing in shared object file",
263  }
264 
265  if (!destroy) {
266  dlclose(libHandle);
267  create = NULL;
268  libHandle = NULL;
269  throw PluginException(
270  "function \"destroy\" missing in shared object file",
272  }
273 }
274 
276  if (libHandle) {
277  dlclose(libHandle);
278  if (dlerror()) {
279  std::ostringstream msgs;
280  msgs << "Error unloading plugin \"" << pluginName
281  << "\". Description of the error: " << dlerror();
282  std::string msg = msgs.str();
283  throw PluginException(
285  }
286  libHandle = NULL;
287  create = NULL;
288  destroy = NULL;
289  sout << "(DD) Successfully unloaded plugin \"" << pluginName << "\"."
290  << std::endl;
291  } else {
292  throw PluginException("Plugin \"" + pluginName + "\" is not loaded.",
294  }
295 }
296 
298  if (libHandle) {
299  unload();
300  }
301 }
#define CHARON_CORE_VERSION_PATCH
charon-core patch version
Definition: configVersion.h:30
File is a shared object but misses required functions.
virtual void unload()
Unloads the plugin.
#define CHARON_CORE_VERSION
charon-core version string
Definition: configVersion.h:32
there is a missmatch between the current charon-core version and the version the plugin was compiled ...
#define CHARON_CORE_VERSION_MINOR
charon-core minor version
Definition: configVersion.h:28
SplitStream sout
Dummy instance for usage in other files (for interface too).
File is damaged, unreadable, not a shared object or the user access rights are insufficient.
#define CHARON_CORE_VERSION_MAJOR
charon-core major version
Definition: configVersion.h:26
bool charon_core_DLL_PUBLIC exists(const std::string &file)
Check if file exists.
Definition: FileTool.cpp:154
void(* destroy)(ParameteredObject *)
Function pointer to the destructor of the plugin.
virtual void load()
Loads the plugin.
ParameteredObject *(* create)(const std::string &, ParameteredObject::template_type)
Function pointer to the constructor of the plugin.
std::vector< std::string > & pluginPaths
Paths where the plugins are stored.
std::string pluginName
The name of the Plugin (without prefix "lib" and without extension)
virtual ~UnixPluginLoader()
Default destructor.
Plugin file doesn't exist or is placed in the wrong directory.
std::string & libSuffix
Lib suffix e.g. _d for debug builds.
PluginVersionCheckLevel
info how to handle version information
While unloading a plugin or deleting an instance: The plugin is not loaded.
PluginManagerInterface::PluginVersionCheckLevel _versionCheck
should the Loader ignore embedded charon-core version strings?
UnixPluginLoader(const std::string &name, std::vector< std::string > &plpaths, std::string &lSuffix, PluginManagerInterface::PluginVersionCheckLevel versionCheck=PluginManagerInterface::PluginVersionIgnore)
Default constructor.
Version information for the plugin is missing while explicit version check was requested.
#define LIBRARY_EXTENSION
os dependent shlib extension (.so or .dylib)
void * libHandle
Points to the loaded library.
Abstract base class for a plugin loader.