Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
DllLoader.cpp
Go to the documentation of this file.
1 /* Copyright (C) 2012 Wildfire Games.
2  * This file is part of 0 A.D.
3  *
4  * 0 A.D. is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * 0 A.D. is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "precompiled.h"
19 
20 #include "DllLoader.h"
21 
22 #include "lib/timer.h"
23 #include "lib/posix/posix_dlfcn.h"
24 #include "ps/CStr.h"
25 #include "ps/CLogger.h"
26 
27 #if OS_MACOSX
29 #endif
30 
31 static void* const HANDLE_UNAVAILABLE = (void*)-1;
32 
33 // directory to search for libraries (optionally set by --libdir at build-time,
34 // optionally overridden by -libdir at run-time in the test executable);
35 // if we don't have an explicit libdir then the linker will look in DT_RUNPATH
36 // (which we set to $ORIGIN) to find it in the executable's directory
37 #ifdef INSTALLED_LIBDIR
38 static CStr g_Libdir = STRINGIZE(INSTALLED_LIBDIR);
39 #else
40 static CStr g_Libdir = "";
41 #endif
42 
43 // note: on Linux, lib is prepended to the SO file name
44 #if OS_UNIX
45 static CStr prefix = "lib";
46 #else
47 static CStr prefix = "";
48 #endif
49 
50 // we usually want to use the same debug/release build type as the
51 // main executable (the library should also be efficient in release builds and
52 // allow easy symbol access in debug builds). however, that version of the
53 // library might be missing, so we check for both.
54 // this works because the interface is binary-compatible.
55 #ifndef NDEBUG
56 static CStr suffixes[] = { "_dbg", "" }; // (order matters)
57 #else
58 static CStr suffixes[] = { "", "_dbg" };
59 #endif
60 
61 // NB: our Windows dlopen() function changes the extension to .dll
62 static CStr extensions[] = {
63  ".so",
64 #if OS_MACOSX
65  ".dylib" // supported by OS X dlopen
66 #endif
67 };
68 
69 // (This class is currently only used by 'Collada' and 'AtlasUI' which follow
70 // the naming/location convention above - it'll need to be changed if we want
71 // to support other DLLs.)
72 
73 static CStr GenerateFilename(const CStr& name, const CStr& suffix, const CStr& extension)
74 {
75  CStr n;
76 
77  if (!g_Libdir.empty())
78  n = g_Libdir + "/";
79 
80 #if OS_MACOSX
82  {
83  // We are in a bundle, in which case the lib directory is ../Frameworks
84  // relative to the binary, so we use a helper function to get the system path
85  // (alternately we could use @executable_path as below, but this seems better)
86  CStr frameworksPath = osx_GetBundleFrameworksPath();
87  if (!frameworksPath.empty())
88  n = frameworksPath + "/";
89  }
90  else
91  {
92  // On OS X, dlopen will search the current working directory for the library to
93  // to load if given only a filename. But the CWD is not guaranteed to be
94  // binaries/system (where our dylibs are placed) and it's not when e.g. the user
95  // launches the game from Finder.
96  // To work around this, we use the @executable_path variable, which should always
97  // resolve to binaries/system, if we're not a bundle.
98  // (see Apple's dyld(1) and dlopen(3) man pages for more info)
99  n = "@executable_path/";
100  }
101 #endif
102 
103  n += prefix + name + suffix + extension;
104  return n;
105 }
106 
107 
108 
109 // @param name base name of the library (excluding prefix/suffix/extension)
110 // @param errors receives descriptions of any and all errors encountered
111 // @return valid handle or 0
112 static void* LoadAnyVariant(const CStr& name, std::stringstream& errors)
113 {
114  for(size_t idxSuffix = 0; idxSuffix < ARRAY_SIZE(suffixes); idxSuffix++)
115  {
116  for(size_t idxExtension = 0; idxExtension < ARRAY_SIZE(extensions); idxExtension++)
117  {
118  CStr filename = GenerateFilename(name, suffixes[idxSuffix], extensions[idxExtension]);
119 
120  // we don't really care when relocations take place, but one of
121  // {RTLD_NOW, RTLD_LAZY} must be specified. go with the former because
122  // it is safer and matches the Windows load behavior.
123  const int flags = RTLD_LOCAL|RTLD_NOW;
124  void* handle = dlopen(filename.c_str(), flags);
125  if(handle)
126  return handle;
127  else
128  errors << "dlopen(" << filename << ") failed: " << dlerror() << "; ";
129  }
130  }
131 
132  return 0; // none worked
133 }
134 
135 
136 DllLoader::DllLoader(const char* name)
137  : m_Name(name), m_Handle(0)
138 {
139 }
140 
142 {
143  if (IsLoaded())
144  dlclose(m_Handle);
145 }
146 
148 {
149  return (m_Handle != 0 && m_Handle != HANDLE_UNAVAILABLE);
150 }
151 
153 {
154  // first time: try to open the shared object
155  // postcondition: m_Handle valid or == HANDLE_UNAVAILABLE.
156  if (m_Handle == 0)
157  {
158  TIMER(L"LoadDLL");
159 
160  std::stringstream errors;
161  m_Handle = LoadAnyVariant(m_Name, errors);
162  if(!m_Handle) // (only report errors if nothing worked)
163  {
164  LOGERROR(L"DllLoader: %hs", errors.str().c_str());
166  }
167  }
168 
169  return (m_Handle != HANDLE_UNAVAILABLE);
170 }
171 
173 {
174  if(!IsLoaded())
175  return;
176 
177  dlclose(m_Handle);
178  m_Handle = 0;
179 }
180 
181 void DllLoader::LoadSymbolInternal(const char* name, void** fptr) const
182 {
183  if(!IsLoaded())
184  {
185  debug_warn(L"Loading symbol from invalid DLL");
186  *fptr = NULL;
188  }
189 
190  *fptr = dlsym(m_Handle, name);
191  if(*fptr == NULL)
193 }
194 
195 void DllLoader::OverrideLibdir(const char* libdir)
196 {
197  g_Libdir = libdir;
198 }
static CStr extensions[]
Definition: DllLoader.cpp:62
#define LOGERROR
Definition: CLogger.h:35
bool osx_IsAppBundleValid()
Check if app is running in a valid bundle.
Definition: osx_bundle.mm:40
static void *const HANDLE_UNAVAILABLE
Definition: DllLoader.cpp:31
void Unload()
Unload the library, if it has been loaded already.
Definition: DllLoader.cpp:172
static CStr prefix
Definition: DllLoader.cpp:47
#define RTLD_LOCAL
Definition: wdlfcn.h:35
const char * m_Name
Definition: DllLoader.h:81
DllLoader(const char *name)
Prepare the DLL loader.
Definition: DllLoader.cpp:136
C++ interface to Cocoa implementation for getting bundle information.
#define ARRAY_SIZE(name)
static CStr GenerateFilename(const CStr &name, const CStr &suffix, const CStr &extension)
Definition: DllLoader.cpp:73
bool IsLoaded() const
Check whether the library has been loaded successfully.
Definition: DllLoader.cpp:147
static CStr suffixes[]
Definition: DllLoader.cpp:56
void * dlopen(const char *so_name, int flags)
Definition: wdlfcn.cpp:64
int dlclose(void *handle)
Definition: wdlfcn.cpp:40
void * dlsym(void *handle, const char *sym_name)
Definition: wdlfcn.cpp:74
void * m_Handle
Definition: DllLoader.h:82
#define RTLD_NOW
Definition: wdlfcn.h:33
#define TIMER(description)
Measures the time taken to execute code up until end of the current scope; displays it via debug_prin...
Definition: timer.h:108
bool LoadDLL()
Attempt to load and initialise the library, if not already.
Definition: DllLoader.cpp:152
std::string osx_GetBundleFrameworksPath()
Get the system path to the bundle&#39;s Frameworks directory.
Definition: osx_bundle.mm:105
static CStr g_Libdir
Definition: DllLoader.cpp:40
const char * extension
Definition: mongoose.cpp:1736
static void * LoadAnyVariant(const CStr &name, std::stringstream &errors)
Definition: DllLoader.cpp:112
static void OverrideLibdir(const char *libdir)
Override the build-time setting of the directory to search for libraries.
Definition: DllLoader.cpp:195
#define STRINGIZE(id)
static Handle handle(size_t idx, u64 tag)
Definition: h_mgr.cpp:121
#define debug_warn(expr)
display the error dialog with the given text.
Definition: debug.h:324
char * dlerror()
Definition: wdlfcn.cpp:47
void LoadSymbolInternal(const char *name, void **fptr) const
Definition: DllLoader.cpp:181